From 50ea60a01cac3a5b63b712c209a70a605d91dae0 Mon Sep 17 00:00:00 2001 From: Klaus Leithoff Date: Tue, 30 Sep 2014 14:06:11 +0000 Subject: [PATCH] * Mail: Resolve ms-tnef attachments if possible, and required backend functionality is assumed to be available --- .../inc/class.emailadmin_imapbase.inc.php | 141 ++++++++++++++++-- 1 file changed, 125 insertions(+), 16 deletions(-) diff --git a/emailadmin/inc/class.emailadmin_imapbase.inc.php b/emailadmin/inc/class.emailadmin_imapbase.inc.php index 142cea97cd..316ba2a781 100644 --- a/emailadmin/inc/class.emailadmin_imapbase.inc.php +++ b/emailadmin/inc/class.emailadmin_imapbase.inc.php @@ -1486,6 +1486,36 @@ class emailadmin_imapbase $headerObject['ATTACHMENTS'][$mime_id]['cid'] = $cid; $headerObject['ATTACHMENTS'][$mime_id]['partID']=$mime_id; if (!isset($headerObject['ATTACHMENTS'][$mime_id]['name']))$headerObject['ATTACHMENTS'][$mime_id]['name']=$part->getName(); + if ($headerObject['ATTACHMENTS'][$mime_id]['name']=='winmail.dat' && + ($headerObject['ATTACHMENTS'][$mime_id]['mimeType']=='application/octet-stream' || + $headerObject['ATTACHMENTS'][$mime_id]['mimeType']=='application/ms-tnef')) + { + $tnefResolved=false; + $tnef_data = $this->getAttachment($headerObject['ATTACHMENTS'][$mime_id]['uid'],$headerObject['ATTACHMENTS'][$mime_id]['partID'],0,false); + $myTnef = $this->tnef_decoder($tnef_data['attachment']); + //error_log(__METHOD__.__LINE__.array2string($myTnef->getParts())); + // Note: MimeId starts with 0, almost always, we cannot use that as winmail_id + // we need to build Something that meets the needs + if ($myTnef) + { + foreach($myTnef->getParts() as $vmime_id => $part) + { + $tnefResolved=true; + $attachment = $part->getAllDispositionParameters(); + + $attachment['mimeType'] = $part->getType(); + $attachment['uid'] = $headerObject['ATTACHMENTS'][$mime_id]['uid']; + $attachment['partID'] = $headerObject['ATTACHMENTS'][$mime_id]['partID']; + $attachment['is_winmail'] = $headerObject['ATTACHMENTS'][$mime_id]['uid'].'@'.$headerObject['ATTACHMENTS'][$mime_id]['partID'].'@'.$vmime_id; + if (!isset($attachment['name'])||empty($attachment['name'])) $attachment['name'] = $part->getName(); + $attachment['size'] = $part->getBytes(); + if (($cid = $part->getContentId())) $attachment['cid'] = $cid; + if (empty($attachment['name'])) $attachment['name'] = (isset($attachment['cid'])&&!empty($attachment['cid'])?$attachment['cid']:lang("unknown").'_Uid'.$_uid.'_Part'.$mime_id).'.'.mime_magic::mime2ext($attachment['mimeType']); + $headerObject['ATTACHMENTS'][$mime_id.'.'.$vmime_id] = $attachment; + } + if ($tnefResolved) unset($headerObject['ATTACHMENTS'][$mime_id]); + } + } //error_log(__METHOD__.' ('.__LINE__.') '.' PartDisposition:'.$mime_id.'->'.array2string($part->getName())); //error_log(__METHOD__.' ('.__LINE__.') '.' PartDisposition:'.$mime_id.'->'.array2string($part->getAllDispositionParameters())); //error_log(__METHOD__.' ('.__LINE__.') '.' Attachment:'.$mime_id.'->'.array2string($headerObject['ATTACHMENTS'][$mime_id])); @@ -4788,6 +4818,7 @@ class emailadmin_imapbase if (!$_structure || !$_structure->contentTypeMap()) return array(); if (!empty($_partID)) $_structure = $_structure->getPart($_partID); $skipParts = array(); + $tnefParts = array(); foreach($_structure->contentTypeMap() as $mime_id => $mime_type) { $part = $_structure->getPart($mime_id); @@ -4817,6 +4848,7 @@ class emailadmin_imapbase // we attempt to fetch "ourselves" if ($_partID==$part->getMimeId() && $part->getPrimaryType()=='message') continue; $attachment = $part->getAllDispositionParameters(); + $attachment['mimeType'] = $mime_type; $attachment['uid'] = $_uid; $attachment['partID'] = $mime_id; @@ -4831,9 +4863,52 @@ class emailadmin_imapbase $attachment['size'] = $part->getBytes(); if (($cid = $part->getContentId())) $attachment['cid'] = $cid; if (empty($attachment['name'])) $attachment['name'] = (isset($attachment['cid'])&&!empty($attachment['cid'])?$attachment['cid']:lang("unknown").'_Uid'.$_uid.'_Part'.$mime_id).'.'.mime_magic::mime2ext($mime_type); - $attachments[] = $attachment; + //error_log(__METHOD__.' ('.__LINE__.') '.' Uid:'.$uid.' Part:'.$_partID.'->'.$mime_id.':'.array2string($attachment)); + //typical winmail.dat attachment is + //Array([size] => 1462762[filename] => winmail.dat[mimeType] => application/ms-tnef[uid] => 100[partID] => 2[name] => winmail.dat) + if ($resolveTNEF && $attachment['mimeType']=='application/ms-tnef') + { + $tnefParts[] = $attachment; + } + else + { + $attachments[] = $attachment; + } } } + if ($resolveTNEF && !empty($tnefParts)) + { + //error_log(__METHOD__.__LINE__.array2string($tnefParts)); + foreach ($tnefParts as $k => $tnp) + { + $tnefResolved=false; + $tnef_data = $this->getAttachment($tnp['uid'],$tnp['partID'],$k,false); + $myTnef = $this->tnef_decoder($tnef_data['attachment']); + //error_log(__METHOD__.__LINE__.array2string($myTnef->getParts())); + // Note: MimeId starts with 0, almost always, we cannot use that as winmail_id + // we need to build Something that meets the needs + if ($myTnef) + { + foreach($myTnef->getParts() as $mime_id => $part) + { + $tnefResolved=true; + $attachment = $part->getAllDispositionParameters(); + + $attachment['mimeType'] = $part->getType(); + $attachment['uid'] = $tnp['uid']; + $attachment['partID'] = $tnp['partID']; + $attachment['is_winmail'] = $tnp['uid'].'@'.$tnp['partID'].'@'.$mime_id; + if (!isset($attachment['name'])||empty($attachment['name'])) $attachment['name'] = $part->getName(); + $attachment['size'] = $part->getBytes(); + if (($cid = $part->getContentId())) $attachment['cid'] = $cid; + if (empty($attachment['name'])) $attachment['name'] = (isset($attachment['cid'])&&!empty($attachment['cid'])?$attachment['cid']:lang("unknown").'_Uid'.$_uid.'_Part'.$mime_id).'.'.mime_magic::mime2ext($attachment['mimeType']); + $attachments[] = $attachment; + } + } + if ($tnefResolved===false) $attachments[]=$tnp; + } + } + //error_log(__METHOD__.__LINE__.array2string($attachments)); return $attachments; } @@ -4845,10 +4920,11 @@ class emailadmin_imapbase * @return boolean|Horde_Mime_part Multipart/Mixed part decoded attachments | * return false if there's no attachments or failure */ - public function tnef_decoder($_uid, $data ) + public function tnef_decoder( $data ) { + if (class_exists('Horde_Compress',true)==false) return false; - $parts_obj = $this->getStructure($_uid); + $parts_obj = new Horde_Mime_part; $parts_obj->setType('multipart/mixed'); $tnef_object = Horde_Compress::factory('tnef'); @@ -4862,7 +4938,7 @@ class emailadmin_imapbase { foreach ($tnef_data as &$data) { - $tmp_part = $this->getStructure($_uid); + $tmp_part = new Horde_Mime_part; $tmp_part->setName($data['name']); $tmp_part->setContents($data['stream']); @@ -4873,9 +4949,11 @@ class emailadmin_imapbase { $type = Horde_Mime_Magic::filenameToMIME($data['name']); } - $tmp_part->setType($data['type']); + $tmp_part->setType($type); + //error_log(__METHOD__.__LINE__.array2string($tmp_part)); $parts_obj->addPart($tmp_part); } + $parts_obj->buildMimeIds(); return $parts_obj; } return false; @@ -4894,6 +4972,7 @@ class emailadmin_imapbase */ function getAttachment($_uid, $_partID, $_winmail_nr=0, $_returnPart=true, $_stream=false) { + //error_log(__METHOD__.__LINE__."Uid:$_uid, PartId:$_partID, WinMailNr:$_winmail_nr, ReturnPart:$_returnPart, Stream:$_stream"); $_folder = ($this->sessionData['mailbox']? $this->sessionData['mailbox'] : $this->icServer->getCurrentMailbox()); $uidsToFetch = new Horde_Imap_Client_Ids(); @@ -4938,27 +5017,57 @@ class emailadmin_imapbase } $ext = mime_magic::mime2ext($structure_mime); if ($ext && stripos($filename,'.')===false && stripos($filename,$ext)===false) $filename = trim($filename).'.'.$ext; + //error_log(__METHOD__.__LINE__.'#'.$structure_mime.'#'.$filename); $attachmentData = array( 'type' => $structure_mime, 'filename' => $filename, 'attachment' => $part->getContents(array('stream'=>$_stream)) ); -/* + // try guessing the mimetype, if we get the application/octet-stream if (strtolower($attachmentData['type']) == 'application/octet-stream') $attachmentData['type'] = mime_magic::filename2mime($attachmentData['filename']); # if the attachment holds a winmail number and is a winmail.dat then we have to handle that. - if ( $filename == 'winmail.dat' && $_winmail_nr > 0 && - ( $wmattach = $this->decode_winmail( $_uid, $_partID, $_winmail_nr ) ) ) + if ( $filename == 'winmail.dat' && $_winmail_nr) { - $ext = mime_magic::mime2ext($wmattach['type']); - if ($ext && stripos($wmattach['name'],'.')===false && stripos($wmattach['name'],$ext)===false) $wmattach['name'] = trim($wmattach['name']).'.'.$ext; - $attachmentData = array( - 'type' => $wmattach['type'], - 'filename' => $wmattach['name'], - 'attachment' => $wmattach['attachment'], - ); + //by now _uid is of type array + $tnefResolved=false; + $wantedPart=$_uid[0].'@'.$_partID; + $myTnef = $this->tnef_decoder($attachmentData['attachment']); + //error_log(__METHOD__.__LINE__.array2string($myTnef->getParts())); + // Note: MimeId starts with 0, almost always, we cannot use that as winmail_id + // we need to build Something that meets the needs + if ($myTnef) + { + foreach($myTnef->getParts() as $mime_id => $part) + { + $tnefResolved=true; + $attachment = $part->getAllDispositionParameters(); + $attachment['mimeType'] = $part->getType(); + //error_log(__METHOD__.__LINE__.'#'.$mime_id.'#'.$filename.'#'.array2string($attachment)); + //error_log(__METHOD__.__LINE__." $_winmail_nr == $wantedPart@$mime_id"); + if ($_winmail_nr == $wantedPart.'@'.$mime_id) + { + //error_log(__METHOD__.__LINE__.'#'.$structure_mime.'#'.$filename.'#'.array2string($attachment)); + if (!isset($attachment['filename'])||empty($attachment['filename'])) $attachment['filename'] = $part->getName(); + if (($cid = $part->getContentId())) $attachment['cid'] = $cid; + if (empty($attachment['filename'])) $attachment['filename'] = (isset($attachment['cid'])&&!empty($attachment['cid'])?$attachment['cid']:lang("unknown").'_Uid'.$_uid.'_Part'.$mime_id).'.'.mime_magic::mime2ext($attachment['mimeType']); + $wmattach = $attachment; + $wmattach['attachment'] = $part->getContents(array('stream'=>$_stream)); + + } + } + } + if ($tnefResolved) + { + $ext = mime_magic::mime2ext($wmattach['mimeType']); + if ($ext && stripos($wmattach['filename'],'.')===false && stripos($wmattach['filename'],$ext)===false) $wmattach['filename'] = trim($wmattach['filename']).'.'.$ext; + $attachmentData = array( + 'type' => $wmattach['mimeType'], + 'filename' => $wmattach['filename'], + 'attachment' => $wmattach['attachment'], + ); + } } -*/ return $attachmentData; }