Return an iterator over the non-main parts of a multipart. Skip the first of each occurrence of text/plain, text/html, multipart/related, or multipart/alternative in the multipart (unless they have a 'Content-Disposition: attachment' header) and include all remaining
(self)
| 1072 | ('multipart', 'related'), |
| 1073 | ('multipart', 'alternative')} |
| 1074 | def iter_attachments(self): |
| 1075 | """Return an iterator over the non-main parts of a multipart. |
| 1076 | |
| 1077 | Skip the first of each occurrence of text/plain, text/html, |
| 1078 | multipart/related, or multipart/alternative in the multipart (unless |
| 1079 | they have a 'Content-Disposition: attachment' header) and include all |
| 1080 | remaining subparts in the returned iterator. When applied to a |
| 1081 | multipart/related, return all parts except the root part. Return an |
| 1082 | empty iterator when applied to a multipart/alternative or a |
| 1083 | non-multipart. |
| 1084 | """ |
| 1085 | maintype, subtype = self.get_content_type().split('/') |
| 1086 | if maintype != 'multipart' or subtype == 'alternative': |
| 1087 | return |
| 1088 | payload = self.get_payload() |
| 1089 | # Certain malformed messages can have content type set to `multipart/*` |
| 1090 | # but still have single part body, in which case payload.copy() can |
| 1091 | # fail with AttributeError. |
| 1092 | try: |
| 1093 | parts = payload.copy() |
| 1094 | except AttributeError: |
| 1095 | # payload is not a list, it is most probably a string. |
| 1096 | return |
| 1097 | |
| 1098 | if maintype == 'multipart' and subtype == 'related': |
| 1099 | # For related, we treat everything but the root as an attachment. |
| 1100 | # The root may be indicated by 'start'; if there's no start or we |
| 1101 | # can't find the named start, treat the first subpart as the root. |
| 1102 | start = self.get_param('start') |
| 1103 | if start: |
| 1104 | found = False |
| 1105 | attachments = [] |
| 1106 | for part in parts: |
| 1107 | if part.get('content-id') == start: |
| 1108 | found = True |
| 1109 | else: |
| 1110 | attachments.append(part) |
| 1111 | if found: |
| 1112 | yield from attachments |
| 1113 | return |
| 1114 | parts.pop(0) |
| 1115 | yield from parts |
| 1116 | return |
| 1117 | # Otherwise we more or less invert the remaining logic in get_body. |
| 1118 | # This only really works in edge cases (ex: non-text related or |
| 1119 | # alternatives) if the sending agent sets content-disposition. |
| 1120 | seen = [] # Only skip the first example of each candidate type. |
| 1121 | for part in parts: |
| 1122 | maintype, subtype = part.get_content_type().split('/') |
| 1123 | if ((maintype, subtype) in self._body_types and |
| 1124 | not part.is_attachment() and subtype not in seen): |
| 1125 | seen.append(subtype) |
| 1126 | continue |
| 1127 | yield part |
| 1128 | |
| 1129 | def iter_parts(self): |
| 1130 | """Return an iterator over all immediate subparts of a multipart. |