From 4098a2bfe464ea3a3286efaacc0b3340c352bd78 Mon Sep 17 00:00:00 2001
From: JediKev <kevin@enhancesoft.com>
Date: Wed, 17 Apr 2019 15:47:24 -0500
Subject: [PATCH] issue: .eml/.msg Attachments

This addresses an issue where `.eml` and `.msg` files on incoming mails are
being dropped. This is due the the mail fetcher that tries to process
`.eml`/`.msg` files and adds them as thread entries rather than adding them
as attachments. This adds a new section that utilizes a new method to fetch
the body of `.eml`/`.msg` files, fetches the subjects of the `.eml`/`.msg`
files as the attachment names, and creates attachments. This preserves the
`.eml` and `.msg` files and adds them to the pertinent thread entries as
attachments.
---
 include/class.mailfetch.php | 51 ++++++++++++++++++++++++++++++++-----
 1 file changed, 45 insertions(+), 6 deletions(-)

diff --git a/include/class.mailfetch.php b/include/class.mailfetch.php
index fec76bc01..2db19fc7b 100644
--- a/include/class.mailfetch.php
+++ b/include/class.mailfetch.php
@@ -384,8 +384,17 @@ class MailFetcher {
         return $header;
     }
 
+    function fetchBody($mid, $index, $encoding) {
+        $body = imap_fetchbody($this->mbox, $mid, $index);
+        if ($body && $encoding)
+            $body = $this->decode($body, $encoding);
+
+        return $body;
+    }
+
     //search for specific mime type parts....encoding is the desired encoding.
-    function getPart($mid, $mimeType, $encoding=false, $struct=null, $partNumber=false, $recurse=-1, $recurseIntoRfc822=true) {
+    function getPart($mid, $mimeType, $encoding=false, $struct=null,
+        $partNumber=false, $recurse=-1, $recurseIntoRfc822=false) {
 
         if(!$struct && $mid)
             $struct=@imap_fetchstructure($this->mbox, $mid);
@@ -475,8 +484,9 @@ class MailFetcher {
 
      */
     function getAttachments($part, $index=0) {
-
-        if($part && !$part->parts) {
+        $ctype = $part ? $this->getMimeType($part) : false;
+        if($part && (!$part->parts
+              || strcasecmp($ctype, 'message/rfc822') === 0)) {
             //Check if the part is an attachment.
             $filename = false;
             if ($part->ifdisposition && $part->ifdparameters
@@ -501,6 +511,12 @@ class MailFetcher {
                 $filename = _S('image').'-'.Misc::randCode(4).'.'.strtolower($part->subtype);
             }
 
+            // Attached message/rfc822 without filename.
+            if (!$filename
+                  && $ctype
+                  && strcasecmp($ctype, 'message/rfc822') === 0)
+                $filename = 'email-message-'.Misc::randCode(4).'.eml';
+
             if($filename) {
                 return array(
                         array(
@@ -658,11 +674,13 @@ class MailFetcher {
 	        return true; //Report success (moved or delete)
         }
 
-        // Parse MS TNEF emails
+        // Process overloaded attachments
         if (($struct = imap_fetchstructure($this->mbox, $mid))
                 && ($attachments = $this->getAttachments($struct))) {
             foreach ($attachments as $i=>$info) {
-                if (0 === strcasecmp('application/ms-tnef', $info['type'])) {
+                switch (strtolower($info['type'])) {
+                // Parse MS TNEF emails
+                case 'application/ms-tnef':
                     try {
                         $data = $this->decode(imap_fetchbody($this->mbox,
                             $mid, $info['index']), $info['encoding']);
@@ -675,6 +693,26 @@ class MailFetcher {
                     } catch (TnefException $ex) {
                         // Noop -- winmail.dat remains an attachment
                     }
+                    break;
+                // Parse attached email message
+                case 'message/rfc822':
+                    try {
+                        // Fetch the header of attached mime message.
+                        $body = $this->fetchBody($mid, $info['index'].'.0',
+                            $info['encoding']);
+                        // Add fake body to make the parser happy
+                        if ($body)
+                             $body.="\n\nJunk";
+
+                        $parser = new Mail_Parse($body);
+                        if ($parser->decode()
+                              && ($subj = $parser->getSubject()))
+                            $attachments[$i]['name'] = $subj.'.eml';
+                    } catch(Exception $ex) {
+                        // Noop -- use random name
+                    }
+                    $body = $parser = null;
+                    break;
                 }
             }
         }
@@ -928,7 +966,8 @@ class MailFetcher {
                 db_query('UPDATE '.EMAIL_TABLE.' SET mail_errors=mail_errors+1, mail_lasterror=NOW() WHERE email_id='.db_input($emailId));
                 if (++$errors>=$MAXERRORS) {
                     //We've reached the MAX consecutive errors...will attempt logins at delayed intervals
-                    $msg="\n"._S('osTicket is having trouble fetching emails from the following mail account').": \n".
+                    // XXX: Translate me
+                    $msg="\nosTicket is having trouble fetching emails from the following mail account: \n".
                         "\n"._S('User').": ".$fetcher->getUsername().
                         "\n"._S('Host').": ".$fetcher->getHost().
                         "\n"._S('Error').": ".$fetcher->getLastError().
-- 
GitLab