+ // message contains multiple parts
+ else if (is_array($structure->parts) && !empty($structure->parts)) {
+ // iterate over parts
+ for ($i=0; $i < count($structure->parts); $i++) {
+ $mail_part = &$structure->parts[$i];
+ $primary_type = $mail_part->ctype_primary;
+ $secondary_type = $mail_part->ctype_secondary;
+
+ // real content-type of message/rfc822
+ if ($mail_part->real_mimetype) {
+ $part_orig_mimetype = $mail_part->mimetype;
+ $part_mimetype = $mail_part->real_mimetype;
+ list($primary_type, $secondary_type) = explode('/', $part_mimetype);
+ }
+ else
+ $part_mimetype = $mail_part->mimetype;
+
+ // multipart/alternative
+ if ($primary_type == 'multipart') {
+ $this->parse_structure($mail_part, true);
+
+ // list message/rfc822 as attachment as well (mostly .eml)
+ if ($part_orig_mimetype == 'message/rfc822' && !empty($mail_part->filename))
+ $this->attachments[] = $mail_part;
+ }
+ // part text/[plain|html] or delivery status
+ else if ((($part_mimetype == 'text/plain' || $part_mimetype == 'text/html') && $mail_part->disposition != 'attachment') ||
+ in_array($part_mimetype, array('message/delivery-status', 'text/rfc822-headers', 'message/disposition-notification'))
+ ) {
+ // Allow plugins to handle also this part
+ $plugin = $this->app->plugins->exec_hook('message_part_structure',
+ array('object' => $this, 'structure' => $mail_part,
+ 'mimetype' => $part_mimetype, 'recursive' => true));
+
+ if ($plugin['abort'])
+ continue;
+
+ if ($part_mimetype == 'text/html') {
+ $got_html_part = true;
+ }
+
+ $mail_part = $plugin['structure'];
+ list($primary_type, $secondary_type) = explode('/', $plugin['mimetype']);
+
+ // add text part if it matches the prefs
+ if (!$this->parse_alternative ||
+ ($secondary_type == 'html' && $this->opt['prefer_html']) ||
+ ($secondary_type == 'plain' && !$this->opt['prefer_html'])
+ ) {
+ $mail_part->type = 'content';
+ $this->parts[] = $mail_part;
+ }
+
+ // list as attachment as well
+ if (!empty($mail_part->filename))
+ $this->attachments[] = $mail_part;
+ }
+ // part message/*
+ else if ($primary_type == 'message') {
+ $this->parse_structure($mail_part, true);
+
+ // list as attachment as well (mostly .eml)
+ if (!empty($mail_part->filename))
+ $this->attachments[] = $mail_part;
+ }
+ // ignore "virtual" protocol parts
+ else if ($primary_type == 'protocol') {
+ continue;
+ }
+ // part is Microsoft Outlook TNEF (winmail.dat)
+ else if ($part_mimetype == 'application/ms-tnef') {
+ foreach ((array)$this->tnef_decode($mail_part) as $tpart) {
+ $this->mime_parts[$tpart->mime_id] = $tpart;
+ $this->attachments[] = $tpart;
+ }
+ }
+ // part is a file/attachment
+ else if (preg_match('/^(inline|attach)/', $mail_part->disposition) ||
+ $mail_part->headers['content-id'] ||
+ ($mail_part->filename &&
+ (empty($mail_part->disposition) || preg_match('/^[a-z0-9!#$&.+^_-]+$/i', $mail_part->disposition)))
+ ) {
+ // skip apple resource forks
+ if ($message_ctype_secondary == 'appledouble' && $secondary_type == 'applefile')
+ continue;
+
+ // part belongs to a related message and is linked
+ if ($mimetype == 'multipart/related'
+ && ($mail_part->headers['content-id'] || $mail_part->headers['content-location'])) {
+ if ($mail_part->headers['content-id'])
+ $mail_part->content_id = preg_replace(array('/^</', '/>$/'), '', $mail_part->headers['content-id']);
+ if ($mail_part->headers['content-location'])
+ $mail_part->content_location = $mail_part->headers['content-base'] . $mail_part->headers['content-location'];
+
+ $this->inline_parts[] = $mail_part;
+ }
+ // attachment encapsulated within message/rfc822 part needs further decoding (#1486743)
+ else if ($part_orig_mimetype == 'message/rfc822') {
+ $this->parse_structure($mail_part, true);
+
+ // list as attachment as well (mostly .eml)
+ if (!empty($mail_part->filename))
+ $this->attachments[] = $mail_part;
+ }
+ // regular attachment with valid content type
+ // (content-type name regexp according to RFC4288.4.2)
+ else if (preg_match('/^[a-z0-9!#$&.+^_-]+\/[a-z0-9!#$&.+^_-]+$/i', $part_mimetype)) {
+ if (!$mail_part->filename)
+ $mail_part->filename = 'Part '.$mail_part->mime_id;
+
+ $this->attachments[] = $mail_part;
+ }
+ // attachment with invalid content type
+ // replace malformed content type with application/octet-stream (#1487767)
+ else if ($mail_part->filename) {
+ $mail_part->ctype_primary = 'application';
+ $mail_part->ctype_secondary = 'octet-stream';
+ $mail_part->mimetype = 'application/octet-stream';
+
+ $this->attachments[] = $mail_part;
+ }
+ }
+ // attachment part as message/rfc822 (#1488026)
+ else if ($mail_part->mimetype == 'message/rfc822') {
+ $this->parse_structure($mail_part);
+ }
+ }