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