/** * Return the rendered information about the Horde_Mime_Part object. * * @return array See parent::render(). */ protected function _renderInfo() { $mdn_id = $this->_mimepart->getMimeId(); $parts = array_keys($this->_mimepart->contentTypeMap()); $status = new IMP_Mime_Status(_("A message you have sent has resulted in a return notification from the recipient.")); $status->icon('info_icon.png', _("Info")); /* RFC 3798 [3]: There are three parts to a delivery status * multipart/report message: * (1) Human readable message * (2) Machine parsable body part (message/disposition-notification) * (3) Original message (optional) */ /* Get the human readable message. */ reset($parts); $part1_id = next($parts); /* Display a link to more detailed message. */ $part2_id = Horde_Mime::mimeIdArithmetic($part1_id, 'next'); $part = $this->getConfigParam('imp_contents')->getMIMEPart($part2_id); if ($part) { $status->addText(sprintf(_("Technical details can be viewed %s."), $this->getConfigParam('imp_contents')->linkViewJS($part, 'view_attach', _("HERE"), array('jstext' => _("Technical details"), 'params' => array('ctype' => 'text/plain', 'mode' => IMP_Contents::RENDER_FULL))))); } $ret[$part2_id] = null; /* Display a link to the sent message. */ $part3_id = Horde_Mime::mimeIdArithmetic($part2_id, 'next'); $part = $this->getConfigParam('imp_contents')->getMIMEPart($part3_id); if ($part) { $status->addText(sprintf(_("The text of the sent message can be viewed %s."), $this->getConfigParam('imp_contents')->linkViewJS($part, 'view_attach', _("HERE"), array('jstext' => _("The text of the sent message"), 'params' => array('ctype' => 'message/rfc822', 'mode' => IMP_Contents::RENDER_FULL))))); foreach (array_keys($part->contentTypeMap()) as $key) { $ret[$key] = null; } } $ret[$mdn_id] = array('data' => '', 'status' => $status, 'type' => 'text/html; charset=' . $this->getConfigParam('charset'), 'wrap' => 'mimePartWrap'); return $ret; }
/** * Render the part based on the view mode. * * @param boolean $inline True if viewing inline. * * @return array See parent::render(). */ protected function _IMPrender($inline) { /* RFC 1740 [4]: There are two parts to an appledouble message: * (1) application/applefile * (2) Data embedded in the Mac file * Since the resource fork is not very useful to us, only provide a * means to download. */ /* Display the resource fork download link. */ $mime_id = $this->_mimepart->getMimeId(); $parts_list = array_keys($this->_mimepart->contentTypeMap()); reset($parts_list); $applefile_id = next($parts_list); $data_id = Horde_Mime::mimeIdArithmetic($applefile_id, 'next'); $applefile_part = $this->_mimepart->getPart($applefile_id); $data_part = $this->_mimepart->getPart($data_id); $data_name = $this->getConfigParam('imp_contents')->getPartName($data_part); $status = new IMP_Mime_Status(array(sprintf(_("This message contains a Macintosh file (named \"%s\")."), $data_name), sprintf(_("The Macintosh resource fork can be downloaded %s."), $this->getConfigParam('imp_contents')->linkViewJS($applefile_part, 'download_attach', _("HERE"), array('jstext' => _("The Macintosh resource fork")))))); $status->icon('mime/apple.png', _("Macintosh File")); /* For inline viewing, attempt to display the data inline. */ $ret = array(); if ($inline && ($disp = $this->getConfigParam('imp_contents')->canDisplay($data_part, IMP_Contents::RENDER_INLINE | IMP_Contents::RENDER_INFO))) { $ret = $this->getConfigParam('imp_contents')->renderMIMEPart($data_id, $disp); } foreach ($parts_list as $val) { if (!isset($ret[$val]) && strcmp($val, $data_id) !== 0) { $ret[$val] = strcmp($val, $mime_id) === 0 ? array('data' => '', 'status' => $status, 'type' => 'text/html; charset=' . $this->getConfigParam('charset'), 'wrap' => 'mimePartWrap') : null; } } return $ret; }
/** * Builds a proper AS mail message object. * * @param Horde_Imap_Client_Mailbox $mbox The IMAP mailbox. * @param Horde_Imap_Client_Data_Fetch $data The fetch results. * @param array $options Additional Options: * - truncation: (integer) Truncate the message body to this length. * DEFAULT: No truncation. * - bodyprefs: (array) Bodyprefs, if sent from device. * DEFAULT: none (No body prefs sent or enforced). * - bodypartprefs: (array) Bodypartprefs, if sent from device. * DEFAULT: none (No body part prefs sent or enforced). * - mimesupport: (integer) Indicates if MIME is supported or not. * Possible values: 0 - Not supported 1 - Only S/MIME or * 2 - All MIME. * DEFAULT: 0 (No MIME support) * - protocolversion: (float) The EAS protocol version to support. * DEFAULT: 2.5 * * @return Horde_ActiveSync_Message_Mail The message object suitable for * streaming to the device. */ protected function _buildMailMessage(Horde_Imap_Client_Mailbox $mbox, Horde_Imap_Client_Data_Fetch $data, $options = array()) { $version = empty($options['protocolversion']) ? Horde_ActiveSync::VERSION_TWOFIVE : $options['protocolversion']; $imap_message = new Horde_ActiveSync_Imap_Message($this->_getImapOb(), $mbox, $data); $eas_message = Horde_ActiveSync::messageFactory('Mail'); // Build To: data (POOMMAIL_TO has a max length of 32768). $to = $imap_message->getToAddresses(); $eas_message->to = array_pop($to['to']); foreach ($to['to'] as $to_atom) { if (strlen($eas_message->to) + strlen($to_atom) > 32768) { break; } $eas_message->to .= ',' . $to_atom; } $eas_message->displayto = implode(';', $to['displayto']); if (empty($eas_message->displayto)) { $eas_message->displayto = $eas_message->to; } // Ensure we don't send broken UTF8 data to the client. It makes clients // angry. And we don't like angry clients. $hdr_charset = $imap_message->getStructure()->getHeaderCharset(); // Fill in other header data try { $eas_message->from = $imap_message->getFromAddress(); } catch (Horde_ActiveSync_Exception $e) { $this->_logger->err($e->getMessage()); } try { $eas_message->cc = $imap_message->getCc(); } catch (Horde_ActiveSync_Exception $e) { $this->_logger->err($e->getMessage()); } try { $eas_message->reply_to = $imap_message->getReplyTo(); } catch (Horde_ActiveSync_Exception $e) { $this->_logger->err($e->getMessage()); } $eas_message->subject = Horde_ActiveSync_Utils::ensureUtf8($imap_message->getSubject(), $hdr_charset); $eas_message->threadtopic = $eas_message->subject; $eas_message->datereceived = $imap_message->getDate(); $eas_message->read = $imap_message->getFlag(Horde_Imap_Client::FLAG_SEEN); // Default to IPM.Note - may change below depending on message content. $eas_message->messageclass = 'IPM.Note'; // Codepage id. MS recommends to always set to UTF-8 when possible. // See http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756%28v=vs.85%29.aspx $eas_message->cpid = Horde_ActiveSync_Message_Mail::INTERNET_CPID_UTF8; // Message importance. First try X-Priority, then Importance since // Outlook sends the later. if ($priority = $imap_message->getHeaders()->getValue('X-priority')) { $priority = preg_replace('/\\D+/', '', $priority); } else { $priority = $imap_message->getHeaders()->getValue('Importance'); } $eas_message->importance = $this->_getEASImportance($priority); // Get the body data. $mbd = $imap_message->getMessageBodyDataObject($options); if ($version == Horde_ActiveSync::VERSION_TWOFIVE) { $eas_message->body = $mbd->plain['body']->stream; $eas_message->bodysize = $mbd->plain['body']->length(true); $eas_message->bodytruncated = $mbd->plain['truncated']; $eas_message->attachments = $imap_message->getAttachments($version); } else { // Get the message body and determine original type. if ($mbd->html) { $eas_message->airsyncbasenativebodytype = Horde_ActiveSync::BODYPREF_TYPE_HTML; } else { $eas_message->airsyncbasenativebodytype = Horde_ActiveSync::BODYPREF_TYPE_PLAIN; } $airsync_body = Horde_ActiveSync::messageFactory('AirSyncBaseBody'); $body_type_pref = $mbd->getBodyTypePreference(); if ($body_type_pref == Horde_ActiveSync::BODYPREF_TYPE_MIME) { $this->_logger->info(sprintf('[%s] Sending MIME Message.', $this->_procid)); // ActiveSync *REQUIRES* all data sent to be in UTF-8, so we // must convert the body parts to UTF-8. Unfortunately if the // email is signed (or encrypted for that matter) we can't // alter the data in anyway or the signature will not be // verified, so we fetch the entire message and hope for the best. if (!$imap_message->isSigned() && !$imap_message->isEncrypted()) { $mime = new Horde_Mime_Part(); if ($mbd->plain) { $plain_mime = new Horde_Mime_Part(); $plain_mime->setType('text/plain'); $plain_mime->setContents($mbd->plain['body']->stream, array('usestream' => true)); $plain_mime->setCharset('UTF-8'); } if ($mbd->html) { $html_mime = new Horde_Mime_Part(); $html_mime->setType('text/html'); $html_mime->setContents($mbd->html['body']->stream, array('usestream' => true)); $html_mime->setCharset('UTF-8'); } // Sanity check the mime type if (!$mbd->html && !empty($plain_mime)) { $mime = $plain_mime; } elseif (!$mbd->plain && !empty($html_mime)) { $mime = $html_mime; } elseif (!empty($plain_mime) && !empty($html_mime)) { $mime->setType('multipart/alternative'); $mime->addPart($plain_mime); $mime->addPart($html_mime); } $html_mime = null; $plain_mime = null; // If we have attachments, create a multipart/mixed wrapper. if ($imap_message->hasAttachments()) { $base = new Horde_Mime_Part(); $base->setType('multipart/mixed'); $base->addPart($mime); $atc = $imap_message->getAttachmentsMimeParts(); foreach ($atc as $atc_part) { $base->addPart($atc_part); } $eas_message->airsyncbaseattachments = $imap_message->getAttachments($version); } else { $base = $mime; } $mime = null; // Populate the EAS body structure with the MIME data, but // remove the Content-Type and Content-Transfer-Encoding // headers since we are building this ourselves. $headers = $imap_message->getHeaders(); $headers->removeHeader('Content-Type'); $headers->removeHeader('Content-Transfer-Encoding'); $airsync_body->data = $base->toString(array('headers' => $headers, 'stream' => true)); $airsync_body->estimateddatasize = $base->getBytes(); } else { // Signed/Encrypted message - can't mess with it at all. $raw = new Horde_ActiveSync_Rfc822($imap_message->getFullMsg(true), false); $airsync_body->estimateddatasize = $raw->getBytes(); $airsync_body->data = $raw->getString(); $eas_message->airsyncbaseattachments = $imap_message->getAttachments($version); } $airsync_body->type = Horde_ActiveSync::BODYPREF_TYPE_MIME; // MIME Truncation // @todo Remove this sanity-check hack in 3.0. This is needed // since truncationsize incorrectly defaulted to a // MIME_TRUNCATION constant and could be cached in the sync-cache. $ts = !empty($options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_MIME]['truncationsize']) ? $options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_MIME]['truncationsize'] : false; $mime_truncation = !empty($ts) && $ts > 9 ? $ts : (!empty($options['truncation']) && $options['truncation'] > 9 ? $options['truncation'] : false); $this->_logger->info(sprintf('[%s] Checking MIMETRUNCATION: %s, ServerData: %s', $this->_procid, $mime_truncation, $airsync_body->estimateddatasize)); if (!empty($mime_truncation) && $airsync_body->estimateddatasize > $mime_truncation) { ftruncate($airsync_body->data, $mime_truncation); $airsync_body->truncated = '1'; } else { $airsync_body->truncated = '0'; } $eas_message->airsyncbasebody = $airsync_body; } elseif ($body_type_pref == Horde_ActiveSync::BODYPREF_TYPE_HTML) { // Sending non MIME encoded HTML message text. $eas_message->airsyncbasebody = $this->_buildHtmlPart($mbd, $airsync_body); $eas_message->airsyncbaseattachments = $imap_message->getAttachments($version); } elseif ($body_type_pref == Horde_ActiveSync::BODYPREF_TYPE_PLAIN) { // Non MIME encoded plaintext $this->_logger->info(sprintf('[%s] Sending PLAINTEXT Message.', $this->_procid)); if (!empty($mbd->plain['size'])) { $airsync_body->estimateddatasize = $mbd->plain['size']; $airsync_body->truncated = $mbd->plain['truncated']; $airsync_body->data = $mbd->plain['body']->stream; $airsync_body->type = Horde_ActiveSync::BODYPREF_TYPE_PLAIN; $eas_message->airsyncbasebody = $airsync_body; } $eas_message->airsyncbaseattachments = $imap_message->getAttachments($version); } // It's legal to have both a BODY and a BODYPART, so we must also // check for that. if ($version > Horde_ActiveSync::VERSION_FOURTEEN && !empty($options['bodypartprefs'])) { $body_part = Horde_ActiveSync::messageFactory('AirSyncBaseBodypart'); $eas_message->airsyncbasebodypart = $this->_buildBodyPart($mbd, $options, $body_part); } if ($version > Horde_ActiveSync::VERSION_TWELVEONE) { $flags = array(); $msgFlags = $this->_getMsgFlags(); foreach ($imap_message->getFlags() as $flag) { if (!empty($msgFlags[Horde_String::lower($flag)])) { $flags[] = $msgFlags[Horde_String::lower($flag)]; } } $eas_message->categories = $flags; } } // Body Preview? Note that this is different from BodyPart's preview if ($version >= Horde_ActiveSync::VERSION_FOURTEEN && !empty($options['bodyprefs']['preview'])) { $mbd->plain['body']->rewind(); $eas_message->airsyncbasebody->preview = $mbd->plain['body']->substring(0, $options['bodyprefs']['preview']); } $mbd = null; // Check for special message types. if ($imap_message->isEncrypted()) { $eas_message->messageclass = 'IPM.Note.SMIME'; } elseif ($imap_message->isSigned()) { $eas_message->messageclass = 'IPM.Note.SMIME.MultipartSigned'; } $part = $imap_message->getStructure(); if ($part->getType() == 'multipart/report') { $ids = array_keys($imap_message->contentTypeMap()); reset($ids); $part1_id = next($ids); $part2_id = Horde_Mime::mimeIdArithmetic($part1_id, 'next'); $lines = explode(chr(13), $imap_message->getBodyPart($part2_id, array('decode' => true))); switch ($part->getContentTypeParameter('report-type')) { case 'delivery-status': foreach ($lines as $line) { if (strpos(trim($line), 'Action:') === 0) { switch (trim(substr(trim($line), 7))) { case 'failed': $eas_message->messageclass = 'REPORT.IPM.NOTE.NDR'; break 2; case 'delayed': $eas_message->messageclass = 'REPORT.IPM.NOTE.DELAYED'; break 2; case 'delivered': $eas_message->messageclass = 'REPORT.IPM.NOTE.DR'; break 2; } } } break; case 'disposition-notification': foreach ($lines as $line) { if (strpos(trim($line), 'Disposition:') === 0) { if (strpos($line, 'displayed') !== false) { $eas_message->messageclass = 'REPORT.IPM.NOTE.IPNRN'; } elseif (strpos($line, 'deleted') !== false) { $eas_message->messageclass = 'REPORT.IPM.NOTE.IPNNRN'; } break; } } } } $part = null; // Check for meeting requests and POOMMAIL_FLAG data if ($version >= Horde_ActiveSync::VERSION_TWELVE) { $eas_message->contentclass = 'urn:content-classes:message'; if ($mime_part = $imap_message->hasiCalendar()) { $data = Horde_ActiveSync_Utils::ensureUtf8($mime_part->getContents(), $mime_part->getCharset()); $vCal = new Horde_Icalendar(); if ($vCal->parsevCalendar($data, 'VCALENDAR', $mime_part->getCharset())) { $classes = $vCal->getComponentClasses(); } else { $classes = array(); } if (!empty($classes['horde_icalendar_vevent'])) { try { $method = $vCal->getAttribute('METHOD'); $eas_message->contentclass = 'urn:content-classes:calendarmessage'; } catch (Horde_Icalendar_Exception $e) { } switch ($method) { case 'REQUEST': case 'PUBLISH': $eas_message->messageclass = 'IPM.Schedule.Meeting.Request'; $mtg = Horde_ActiveSync::messageFactory('MeetingRequest'); $mtg->fromvEvent($vCal); $eas_message->meetingrequest = $mtg; break; case 'REPLY': try { $reply_status = $this->_getiTipStatus($vCal); switch ($reply_status) { case 'ACCEPTED': $eas_message->messageclass = 'IPM.Schedule.Meeting.Resp.Pos'; break; case 'DECLINED': $eas_message->messageclass = 'IPM.Schedule.Meeting.Resp.Neg'; break; case 'TENTATIVE': $eas_message->messageclass = 'IPM.Schedule.Meeting.Resp.Tent'; } $mtg = Horde_ActiveSync::messageFactory('MeetingRequest'); $mtg->fromvEvent($vCal); $eas_message->meetingrequest = $mtg; } catch (Horde_ActiveSync_Exception $e) { $this->_logger->err($e->getMessage()); } } } } if ($imap_message->getFlag(Horde_Imap_Client::FLAG_FLAGGED)) { $poommail_flag = Horde_ActiveSync::messageFactory('Flag'); $poommail_flag->subject = $imap_message->getSubject(); $poommail_flag->flagstatus = Horde_ActiveSync_Message_Flag::FLAG_STATUS_ACTIVE; $poommail_flag->flagtype = Horde_Imap_Client::FLAG_FLAGGED; $eas_message->flag = $poommail_flag; } } if ($version >= Horde_ActiveSync::VERSION_FOURTEEN) { $eas_message->messageid = $imap_message->getHeaders()->getValue('Message-ID'); $eas_message->forwarded = $imap_message->getFlag(Horde_Imap_Client::FLAG_FORWARDED); $eas_message->answered = $imap_message->getFlag(Horde_Imap_Client::FLAG_ANSWERED); } $imap_message = null; return $eas_message; }
/** * Render out the currently set contents. * * @param boolean $inline Are we viewing inline? * * @return array See parent::render(). */ protected function _IMPrender($inline) { $base_id = $this->_mimepart->getMimeId(); $subparts = $this->_mimepart->contentTypeMap(); $display_ids = $ret = array(); $prefer_plain = $GLOBALS['registry']->getView() == Horde_Registry::VIEW_MINIMAL || $GLOBALS['prefs']->getValue('alternative_display') == 'text'; /* Look for a displayable part. RFC: show the LAST choice that can be * displayed inline. If an alternative is itself a multipart, the user * agent is allowed to show that alternative, an earlier alternative, * or both. If we find a multipart alternative that contains at least * one viewable part, we will display all viewable subparts of that * alternative. */ $imp_contents = $this->getConfigParam('imp_contents'); foreach ($subparts as $mime_id => $type) { $ret[$mime_id] = null; if (strcmp($base_id, $mime_id) !== 0 && $imp_contents->canDisplay($mime_id, $inline ? IMP_Contents::RENDER_INLINE : IMP_Contents::RENDER_FULL) && (!$prefer_plain || $type != 'text/html' && strpos($type, 'text/') === 0)) { $display_ids[strval($mime_id)] = true; } } /* If we found no IDs, return now. */ if (empty($display_ids)) { $ret[$base_id] = array('data' => '', 'status' => new IMP_Mime_Status(_("There are no alternative parts that can be displayed inline.")), 'type' => 'text/html; charset=' . $this->getConfigParam('charset')); return $ret; } /* If the last viewable message exists in a subpart, back up to the * base multipart and display all viewable parts in that multipart. * Else, display the single part. */ end($display_ids); $curr_id = key($display_ids); while (!is_null($curr_id) && strcmp($base_id, $curr_id) !== 0) { if (isset($subparts[$curr_id])) { $disp_id = $curr_id; } $curr_id = Horde_Mime::mimeIdArithmetic($curr_id, 'up'); } /* At this point, $ret contains stubs for all parts living in the base * alternative part. * Go through all subparts of displayable part and make sure all parts * are rendered. Parts not rendered will be marked as not being * handled by this viewer (Bug #9365). */ $render_part = $this->_mimepart->getPart($disp_id); $need_render = $subparts = $render_part->contentTypeMap(); foreach (array_keys($subparts) as $val) { if (isset($display_ids[$val]) && isset($need_render[$val])) { $render = $this->getConfigParam('imp_contents')->renderMIMEPart($val, $inline ? IMP_Contents::RENDER_INLINE : IMP_Contents::RENDER_FULL); foreach (array_keys($render) as $id) { unset($need_render[$id]); if (!$inline) { if (!is_null($render[$id])) { return array($base_id => $render[$id]); } } else { $ret[$id] = $render[$id]; } } } } unset($need_render[$disp_id]); foreach (array_keys($need_render) as $val) { unset($ret[$val]); } return $inline ? $ret : null; }
/** * Return the rendered information about the Horde_Mime_Part object. * * @return array See parent::render(). */ protected function _renderInfo() { $parts = array_keys($this->_mimepart->contentTypeMap()); /* RFC 3464 [2]: There are three parts to a delivery status * multipart/report message: * (1) Human readable message * (2) Machine parsable body part (message/delivery-status) * (3) Returned message (optional) * * Information on the message status is found in the 'Action' field * located in part #2 (RFC 3464 [2.3.3]). It can be either 'failed', * 'delayed', 'delivered', 'relayed', or 'expanded'. */ if (count($parts) < 2) { return array(); } reset($parts); $part1_id = next($parts); $part2_id = Horde_Mime::mimeIdArithmetic($part1_id, 'next'); $part3_id = Horde_Mime::mimeIdArithmetic($part2_id, 'next'); /* Get the action first - it appears in the second part. */ $action = null; $part2 = $this->getConfigParam('imp_contents')->getMIMEPart($part2_id); foreach (explode("\n", $part2->getContents()) as $line) { if (stristr($line, 'Action:') !== false) { $action = strtolower(trim(substr($line, strpos($line, ':') + 1))); if (strpos($action, ' ') !== false) { $action = substr($action, 0, strpos($action, ' ')); } break; } } if (is_null($action)) { return array(); } /* Get the correct text strings for the action type. */ switch ($action) { case 'failed': case 'delayed': $status = new IMP_Mime_Status(array(_("ERROR: Your message could not be delivered."), sprintf(_("Technical error details can be viewed %s."), $this->getConfigParam('imp_contents')->linkViewJS($part2, 'view_attach', _("HERE"), array('jstext' => _("Technical details"), 'params' => array('ctype' => 'text/plain', 'mode' => IMP_Contents::RENDER_FULL)))))); $status->action(IMP_Mime_Status::ERROR); $msg_link = _("The text of the returned message can be viewed %s."); $msg_link_status = _("The text of the returned message"); break; case 'delivered': case 'expanded': case 'relayed': $status = new IMP_Mime_Status(array(_("Your message was successfully delivered."), sprintf(_("Technical message details can be viewed %s."), $this->getConfigParam('imp_contents')->linkViewJS($part2, 'view_attach', _("HERE"), array('jstext' => _("Technical details"), 'params' => array('ctype' => 'text/x-simple', 'mode' => IMP_Contents::RENDER_FULL)))))); $status->action(IMP_Mime_Status::SUCCESS); $msg_link = _("The text of the message can be viewed %s."); $msg_link_status = _("The text of the message"); break; default: return array(); } /* Display a link to the returned message, if it exists. */ $part3 = $this->getConfigParam('imp_contents')->getMIMEPart($part3_id); if ($part3) { $status->addText(sprintf($msg_link, $this->getConfigParam('imp_contents')->linkViewJS($part3, 'view_attach', _("HERE"), array('jstext' => $msg_link_status, 'params' => array('ctype' => 'message/rfc822'))))); } $ret = array_fill_keys(array_diff($parts, array($part1_id)), null); $ret[$this->_mimepart->getMimeId()] = array('data' => '', 'status' => $status, 'type' => 'text/html; charset=' . $this->getConfigParam('charset'), 'wrap' => 'mimePartWrap'); return $ret; }
/** * Generates HTML output for 'multipart/signed' MIME parts. * * @return string The HTML output. */ protected function _outputPGPSigned() { global $conf, $injector, $prefs, $registry, $session; $partlist = array_keys($this->_mimepart->contentTypeMap()); $base_id = reset($partlist); $signed_id = next($partlist); $sig_id = Horde_Mime::mimeIdArithmetic($signed_id, 'next'); if (!$prefs->getValue('use_pgp') || empty($conf['gnupg']['path'])) { return array($sig_id => null); } $status = new IMP_Mime_Status(); $status->addText(_("The data in this part has been digitally signed via PGP.")); $status->icon('mime/encryption.png', 'PGP'); $ret = array($base_id => array('data' => '', 'nosummary' => true, 'status' => array($status), 'type' => 'text/html; charset=' . $this->getConfigParam('charset'), 'wrap' => 'mimePartWrap'), $sig_id => null); if ($prefs->getValue('pgp_verify') || $injector->getInstance('Horde_Variables')->pgp_verify_msg) { $imp_contents = $this->getConfigParam('imp_contents'); $sig_part = $imp_contents->getMIMEPart($sig_id); $status2 = new IMP_Mime_Status(); if (!$sig_part) { $status2->action(IMP_Mime_Status::ERROR); $sig_text = _("This digitally signed message is broken."); $ret[$base_id]['wrap'] = 'mimePartWrapInvalid'; } else { /* Close session, since this may be a long-running * operation. */ $session->close(); try { $imp_pgp = $injector->getInstance('IMP_Crypt_Pgp'); if ($sig_raw = $sig_part->getMetadata(Horde_Crypt_Pgp_Parse::SIG_RAW)) { $sig_result = $imp_pgp->verifySignature($sig_raw, $this->_getSender()->bare_address, null, $sig_part->getMetadata(Horde_Crypt_Pgp_Parse::SIG_CHARSET)); } else { $stream = $imp_contents->isEmbedded($signed_id) ? $this->_mimepart->getMetadata(self::PGP_SIGN_ENC) : $imp_contents->getBodyPart($signed_id, array('mimeheaders' => true, 'stream' => true))->data; rewind($stream); stream_filter_register('horde_eol', 'Horde_Stream_Filter_Eol'); stream_filter_append($stream, 'horde_eol', STREAM_FILTER_READ, array('eol' => Horde_Mime_Part::RFC_EOL)); $sig_result = $imp_pgp->verifySignature(stream_get_contents($stream), $this->_getSender()->bare_address, $sig_part->getContents()); } $status2->action(IMP_Mime_Status::SUCCESS); $sig_text = $sig_result->message; $ret[$base_id]['wrap'] = 'mimePartWrapValid'; } catch (Horde_Exception $e) { $status2->action(IMP_Mime_Status::ERROR); $sig_text = $e->getMessage(); $ret[$base_id]['wrap'] = 'mimePartWrapInvalid'; } } $status2->addText($this->_textFilter($sig_text, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::NOHTML))); $ret[$base_id]['status'][] = $status2; } else { switch ($registry->getView()) { case Horde_Registry::VIEW_BASIC: $status->addText(Horde::link(Horde::selfUrlParams()->add(array('pgp_verify_msg' => 1))) . _("Click HERE to verify the message.") . '</a>'); break; case Horde_Registry::VIEW_DYNAMIC: $status->addText(Horde::link('#', '', 'pgpVerifyMsg') . _("Click HERE to verify the message.") . '</a>'); break; } } return $ret; }
/** * Return the entire part in MIME format. * * @param array $options Additional options: * - canonical: (boolean) Returns the encoded part in strict RFC 822 & * 2045 output - namely, all newlines end with the * canonical <CR><LF> sequence. * DEFAULT: false * - defserver: (string) The default server to use when creating the * header string. * DEFAULT: none * - encode: (integer) A mask of allowable encodings. * DEFAULT: self::ENCODE_7BIT * - headers: (mixed) Include the MIME headers? If true, create a new * headers object. If a Horde_Mime_Headers object, add MIME * headers to this object. If a string, use the string * verbatim. * DEFAULT: true * - id: (string) Return only this MIME ID part. * DEFAULT: Returns the base part. * - stream: (boolean) Return a stream resource. * DEFAULT: false * * @return mixed The MIME string (returned as a resource if $stream is * true). */ public function toString($options = array()) { $eol = $this->getEOL(); $isbase = true; $oldbaseptr = null; $parts = $parts_close = array(); if (isset($options['id'])) { $id = $options['id']; if (!($part = $this[$id])) { return $part; } unset($options['id']); $contents = $part->toString($options); $prev_id = Horde_Mime::mimeIdArithmetic($id, 'up', array('norfc822' => true)); $prev_part = $prev_id == $this->getMimeId() ? $this : $this[$prev_id]; if (!$prev_part) { return $contents; } $boundary = trim($this->getContentTypeParameter('boundary'), '"'); $parts = array($eol . '--' . $boundary . $eol, $contents); if (!isset($this[Horde_Mime::mimeIdArithmetic($id, 'next')])) { $parts[] = $eol . '--' . $boundary . '--' . $eol; } } else { if ($isbase = empty($options['_notbase'])) { $headers = !empty($options['headers']) ? $options['headers'] : false; if (empty($options['encode'])) { $options['encode'] = null; } if (empty($options['defserver'])) { $options['defserver'] = null; } $options['headers'] = true; $options['_notbase'] = true; } else { $headers = true; $oldbaseptr =& $options['_baseptr']; } $this->_temp['toString'] = ''; $options['_baseptr'] =& $this->_temp['toString']; /* Any information about a message is embedded in the message * contents themself. Simply output the contents of the part * directly and return. */ $ptype = $this->getPrimaryType(); if ($ptype == 'message') { $parts[] = $this->_contents; } else { if (!empty($this->_contents)) { $encoding = $this->_getTransferEncoding($options['encode']); switch ($encoding) { case '8bit': if (empty($options['_baseptr'])) { $options['_baseptr'] = '8bit'; } break; case 'binary': $options['_baseptr'] = 'binary'; break; } $parts[] = $this->_transferEncode($this->_contents, $encoding); /* If not using $this->_contents, we can close the stream * when finished. */ if ($this->_temp['transferEncodeClose']) { $parts_close[] = end($parts); } } /* Deal with multipart messages. */ if ($ptype == 'multipart') { if (empty($this->_contents)) { $parts[] = 'This message is in MIME format.' . $eol; } $boundary = trim($this->getContentTypeParameter('boundary'), '"'); /* If base part is multipart/digest, children should not * have content-type (automatically treated as * message/rfc822; RFC 2046 [5.1.5]). */ if ($this->getSubType() === 'digest') { $options['is_digest'] = true; } foreach ($this as $part) { $parts[] = $eol . '--' . $boundary . $eol; $tmp = $part->toString($options); if ($part->getEOL() != $eol) { $tmp = $this->replaceEOL($tmp, $eol, !empty($options['stream'])); } if (!empty($options['stream'])) { $parts_close[] = $tmp; } $parts[] = $tmp; } $parts[] = $eol . '--' . $boundary . '--' . $eol; } } if (is_string($headers)) { array_unshift($parts, $headers); } elseif ($headers) { $hdr_ob = $this->addMimeHeaders(array('encode' => $options['encode'], 'headers' => $headers === true ? null : $headers)); if (!$isbase && !empty($options['is_digest'])) { unset($hdr_ob['content-type']); } if (!empty($this->_temp['toString'])) { $hdr_ob->addHeader('Content-Transfer-Encoding', $this->_temp['toString']); } array_unshift($parts, $hdr_ob->toString(array('canonical' => $eol == self::RFC_EOL, 'charset' => $this->getHeaderCharset(), 'defserver' => $options['defserver']))); } } $newfp = $this->_writeStream($parts); array_map('fclose', $parts_close); if (!is_null($oldbaseptr)) { switch ($this->_temp['toString']) { case '8bit': if (empty($oldbaseptr)) { $oldbaseptr = '8bit'; } break; case 'binary': $oldbaseptr = 'binary'; break; } } if ($isbase && !empty($options['canonical'])) { return $this->replaceEOL($newfp, self::RFC_EOL, !empty($options['stream'])); } return empty($options['stream']) ? $this->_readStream($newfp) : $newfp; }
/** * Parse signed data. * * @param boolean $sig_only Only do signature checking? * * @return mixed See self::_getEmbeddedMimeParts(). */ protected function _parseSignedData($sig_only = false) { $partlist = array_keys($this->_mimepart->contentTypeMap()); $base_id = reset($partlist); $data_id = next($partlist); $sig_id = Horde_Mime::mimeIdArithmetic($data_id, 'next'); /* Initialize inline data. */ $status = new IMP_Mime_Status(_("The data in this part has been digitally signed via S/MIME.")); $status->icon('mime/encryption.png', 'S/MIME'); $cache = $this->getConfigParam('imp_contents')->getViewCache(); $cache->smime[$base_id] = array('sig' => $sig_id, 'status' => $status, 'wrap' => 'mimePartWrap'); if (!$GLOBALS['prefs']->getValue('use_smime')) { $status->addText(_("S/MIME support is not enabled so the digital signature is unable to be verified.")); return null; } /* Sanity checking to make sure MIME structure is correct. */ if (!in_array($sig_id, $partlist)) { $status->action(IMP_Mime_Status::ERROR); $cache->smime[$base_id]['wrap'] = 'mimePartWrapInvalid'; $status->addText(_("Invalid S/MIME data.")); /* This will suppress displaying the invalid part. */ $cache->smime[$base_id]['sig'] = $data_id; return null; } $imp_contents = $this->getConfigParam('imp_contents'); $stream = $imp_contents->isEmbedded($base_id) ? $this->_mimepart->getMetadata('imp-smime-decrypt')->stream : $this->_getPartStream($base_id); $raw_text = $this->_mimepart->replaceEOL($stream, Horde_Mime_Part::RFC_EOL); $this->_initSmime(); $sig_result = null; if ($GLOBALS['prefs']->getValue('smime_verify') || $GLOBALS['injector']->getInstance('Horde_Variables')->smime_verify_msg) { try { $sig_result = $this->_impsmime->verifySignature($raw_text); if ($sig_result->verify) { $status->action(IMP_Mime_Status::SUCCESS); } else { $status->action(IMP_Mime_Status::WARNING); } $cache->smime[$base_id]['wrap'] = 'mimePartWrapValid'; $email = is_array($sig_result->email) ? implode(', ', $sig_result->email) : $sig_result->email; $status->addText($sig_result->msg); if (!empty($sig_result->cert)) { $cert = $this->_impsmime->parseCert($sig_result->cert); if (isset($cert['certificate']['subject']['CommonName']) && strcasecmp($email, $cert['certificate']['subject']['CommonName']) !== 0) { $email = $cert['certificate']['subject']['CommonName'] . ' (' . trim($email) . ')'; } } if (!empty($sig_result->cert) && isset($sig_result->email) && $GLOBALS['registry']->hasMethod('contacts/addField') && $GLOBALS['prefs']->getValue('add_source')) { $status->addText(sprintf(_("Sender: %s"), $imp_contents->linkViewJS($this->_mimepart, 'view_attach', htmlspecialchars(strlen($email) ? $email : $sig_result->email), array('jstext' => _("View certificate details"), 'params' => array('mode' => IMP_Contents::RENDER_INLINE, 'view_smime_key' => 1))))); try { $this->_impsmime->getPublicKey($sig_result->email); } catch (Horde_Exception $e) { $imple = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Imple')->create('IMP_Ajax_Imple_ImportEncryptKey', array('mime_id' => $base_id, 'muid' => strval($imp_contents->getIndicesOb()), 'type' => 'smime')); $status->addText(Horde::link('#', '', '', '', '', '', '', array('id' => $imple->getDomId())) . _("Save the certificate to your Address Book.") . '</a>'); } } elseif (strlen($email)) { $status->addText(sprintf(_("Sender: %s"), htmlspecialchars($email))); } } catch (Horde_Exception $e) { $status->action(IMP_Mime_Status::ERROR); $cache->smime[$base_id]['wrap'] = 'mimePartWrapInvalid'; $status->addText($e->getMessage()); } } else { switch ($GLOBALS['registry']->getView()) { case Horde_Registry::VIEW_BASIC: $status->addText(Horde::link(Horde::selfUrlParams()->add('smime_verify_msg', 1)) . _("Click HERE to verify the data.") . '</a>'); break; case Horde_Registry::VIEW_DYNAMIC: $status->addText(Horde::link('#', '', 'smimeVerifyMsg') . _("Click HERE to verify the data.") . '</a>'); break; } } if ($sig_only) { return; } $subpart = $imp_contents->getMIMEPart($sig_id); if (empty($subpart)) { try { $msg_data = $this->_impsmime->extractSignedContents($raw_text); $subpart = Horde_Mime_Part::parseMessage($msg_data, array('forcemime' => true)); } catch (Horde_Exception $e) { $status->addText($e->getMessage()); return null; } } return $subpart; }
/** * Find a MIME type in parent parts. * * @param string $id The MIME ID to begin the search at. * @param string $type The MIME type to search for. * * @return mixed Either the requested MIME part, or null if not found. */ public function findMimeType($id, $type) { $id = Horde_Mime::mimeIdArithmetic($id, 'up'); while (!is_null($id)) { if (($part = $this->getMIMEPart($id, array('nocontents' => true))) && $part->getType() == $type) { return $part; } $id = Horde_Mime::mimeIdArithmetic($id, 'up'); } return null; }