diff --git a/twisted/mail/imap4.py b/twisted/mail/imap4.py
--- a/twisted/mail/imap4.py
+++ b/twisted/mail/imap4.py
@@ -4843,57 +4843,20 @@ def getBodyStructure(msg, extended=False
     else:
         major = minor = None
 
-
     size = str(msg.getSize())
     unquotedAttrs = [(k, unquote(v)) for (k, v) in attrs.iteritems()]
-    result = [
-        major, minor,                       # Main and Sub MIME types
-        unquotedAttrs,                      # content-type parameter list
-        headers.get('content-id'),
-        headers.get('content-description'),
-        headers.get('content-transfer-encoding'),
-        size,                               # Number of octets total
-    ]
-
-    if major is not None:
-        if major.lower() == 'text':
-            result.append(str(getLineCount(msg)))
-        elif (major.lower(), minor.lower()) == ('message', 'rfc822'):
-            contained = msg.getSubPart(0)
-            result.append(getEnvelope(contained))
-            result.append(getBodyStructure(contained, False))
-            result.append(str(getLineCount(contained)))
-
-    if not extended or major is None:
-        return result
-
-    if major.lower() != 'multipart':
-        headers = 'content-md5', 'content-disposition', 'content-language'
-        headers = msg.getHeaders(False, *headers)
-        disp = headers.get('content-disposition')
-
-        # XXX - I dunno if this is really right
-        if disp:
-            disp = disp.split('; ')
-            if len(disp) == 1:
-                disp = (disp[0].lower(), None)
-            elif len(disp) > 1:
-                disp = (disp[0].lower(), [x.split('=') for x in disp[1:]])
-
-        result.append(headers.get('content-md5'))
-        result.append(disp)
-        result.append(headers.get('content-language'))
-    else:
-        result = [result]
+
+    if major is not None and major.lower() == 'multipart':
+        result = []
         try:
             i = 0
             while True:
                 submsg = msg.getSubPart(i)
-                result.append(getBodyStructure(submsg))
+                result.append(getBodyStructure(submsg, extended=True))
                 i += 1
         except IndexError:
             result.append(minor)
-            result.append(attrs.items())
+            result.append(unquotedAttrs)
 
             # XXX - I dunno if this is really right
             headers = msg.getHeaders(False, 'content-disposition', 'content-language')
@@ -4907,6 +4870,44 @@ def getBodyStructure(msg, extended=False
 
             result.append(disp)
             result.append(headers.get('content-language'))
+        return result
+
+    result = [
+        major, minor,                       # Main and Sub MIME types
+        unquotedAttrs,                      # content-type parameter list
+        headers.get('content-id'),
+        headers.get('content-description'),
+        headers.get('content-transfer-encoding'),
+        size,                               # Number of octets total
+    ]
+
+    if major is not None:
+        if major.lower() == 'text':
+            result.append(str(getLineCount(msg)))
+        elif (major.lower(), minor.lower()) == ('message', 'rfc822'):
+            contained = msg.getSubPart(0)
+            result.append(getEnvelope(contained))
+            result.append(getBodyStructure(contained, False))
+            result.append(str(getLineCount(contained)))
+
+    if not extended or major is None:
+        return result
+
+    headers = 'content-md5', 'content-disposition', 'content-language'
+    headers = msg.getHeaders(False, *headers)
+    disp = headers.get('content-disposition')
+
+    # XXX - I dunno if this is really right
+    if disp:
+        disp = disp.split('; ')
+        if len(disp) == 1:
+            disp = (disp[0].lower(), None)
+        elif len(disp) > 1:
+            disp = (disp[0].lower(), [x.split('=') for x in disp[1:]])
+
+    result.append(headers.get('content-md5'))
+    result.append(disp)
+    result.append(headers.get('content-language'))
 
     return result
 
diff --git a/twisted/mail/test/test_imap.py b/twisted/mail/test/test_imap.py
--- a/twisted/mail/test/test_imap.py
+++ b/twisted/mail/test/test_imap.py
@@ -3531,6 +3531,31 @@ class NewFetchTestCase(unittest.TestCase
     def testFetchBodyStructureUID(self):
         return self.testFetchBodyStructure(1)
 
+    def testFetchBodyStructureMultipart(self, uid=0):
+        self.function = self.client.fetchBodyStructure
+        self.messages = '3:9,10:*'
+        innerMessage = FakeyMessage({
+                'content-type': 'text/plain; name=thing; key="value"',
+                'content-id': 'this-is-the-content-id',
+                'content-description': 'describing-the-content-goes-here!',
+                'content-transfer-encoding': '8BIT',
+                'content-language': 'fr',
+            }, (), '', 'Body\nText\nGoes\nHere\n', 919293, None)
+        self.msgObjs = [FakeyMessage({
+                'content-type': 'multipart/mixed; boundary="xyz"',
+                'content-language': 'en',
+            }, (), '', '', 919293, [innerMessage])]
+        self.expected = {0: {'BODYSTRUCTURE': [
+            ['text', 'plain', [['name', 'thing'], ['key', 'value']],
+             'this-is-the-content-id', 'describing-the-content-goes-here!',
+             '8BIT', '20', '4', None, None, 'fr'],
+            'mixed', [['boundary', 'xyz']], None, 'en'
+            ]}}
+        return self._fetchWork(uid)
+
+    def testFetchBodyStructureMultipartUID(self):
+        return self.testFetchBodyStructureMultipart(1)
+
     def testFetchSimplifiedBody(self, uid=0):
         self.function = self.client.fetchSimplifiedBody
         self.messages = '21'
