Finds the main "body" text part (if any) in a message. "Body" data is the
first text part under this part. Considers only body data that should
be displayed as the main body on an EAS client. I.e., this ignores any
text parts contained withing "attachment" parts such as messages/rfc822
attachments.
/** * Determine which parts we need, and fetches them from the IMAP client. * Takes into account the available parts and the BODYPREF/BODYPARTPREF * options. */ protected function _getParts() { // Look for the parts we need. We try to detect and fetch only the parts // we need, while ensuring we have something to return. So, e.g., if we // don't have BODYPREF_TYPE_HTML, we only request plain text, but if we // can't find plain text but we have a html body, fetch that anyway. $text_id = $this->_basePart->findBody('plain'); $html_id = $this->_basePart->findBody('html'); // Deduce which part(s) we need to request. $want_html_text = $this->_wantHtml(); $want_plain_text = $this->_wantPlainText($html_id, $want_html_text); $want_html_as_plain = false; if (!empty($text_id) && $want_plain_text) { $text_body_part = $this->_basePart->getPart($text_id); } elseif ($want_plain_text && !empty($html_id) && empty($this->_options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_MIME])) { $want_html_text = true; $want_html_as_plain = true; } if (!empty($html_id) && $want_html_text) { $html_body_part = $this->_basePart->getPart($html_id); } // Make sure we have truncation if needed. if (empty($this->_options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_PLAIN]) && !empty($this->_options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_HTML]) && $want_plain_text && $want_html_text) { // We only have HTML truncation data, requested HTML body but only // have plaintext. $this->_options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_PLAIN] = $this->_options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_HTML]; } // Fetch the data from the IMAP client. $data = $this->_fetchData(array('html_id' => $html_id, 'text_id' => $text_id)); if (!empty($text_id) && $want_plain_text) { $this->_plain = $this->_getPlainPart($data, $text_body_part); } if (!empty($html_id) && $want_html_text) { $results = $this->_getHtmlPart($data, $html_body_part, $want_html_as_plain); $this->_html = !empty($results['html']) ? $results['html'] : null; $this->_plain = !empty($results['plain']) ? $results['plain'] : null; } if (!empty($this->_options['bodypartprefs'])) { $this->_bodyPart = $this->_getBodyPart($data, !empty($html_id) ? $html_body_part : $text_body_part, empty($html_id)); } $text_body_part = null; $html_body_part = null; }
/** * Determine which parts we need, and fetches them from the IMAP client. * Takes into account the available parts and the BODYPREF/BODYPARTPREF * options. */ protected function _getParts() { // Look for the parts we need. We try to detect and fetch only the parts // we need, while ensuring we have something to return. So, e.g., if we // don't have BODYPREF_TYPE_HTML, we only request plain text, but if we // can't find plain text but we have a html body, fetch that anyway. // // If this is any type of Report (like a NDR) we can't use findBody // since some MTAs generate MDRs with no explicit mime type in the // human readable portion (the first part). We assume the MDR contains // three parts as specified in the RFC: (1) A human readable part, (2) // A machine parsable body Machine parsable body part // [message/disposition-notification] and (3) The (optional) original // message [message/rfc822] switch ($this->_basePart->getType()) { case 'message/disposition-notification': // OL may send this without an appropriate multipart/report wrapper. // Not sure what to do about this yet. Probably parse the machine // part and write out some basic text? break; case 'multipart/report': $iterator = $this->_basePart->partIterator(false); $iterator->rewind(); if (!($curr = $iterator->current())) { break; } $text_id = $curr->getMimeId(); $html_id = null; break; default: $text_id = $this->_basePart->findBody('plain'); $html_id = $this->_basePart->findBody('html'); } // Deduce which part(s) we need to request. $want_html_text = $this->_wantHtml(); $want_plain_text = $this->_wantPlainText($html_id, $want_html_text); $want_html_as_plain = false; if (!empty($text_id) && $want_plain_text) { $text_body_part = $this->_basePart->getPart($text_id); } elseif ($want_plain_text && !empty($html_id) && empty($this->_options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_MIME])) { $want_html_text = true; $want_html_as_plain = true; } if (!empty($html_id) && $want_html_text) { $html_body_part = $this->_basePart->getPart($html_id); } // Make sure we have truncation if needed. if (empty($this->_options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_PLAIN]) && !empty($this->_options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_HTML]) && $want_plain_text && $want_html_text) { // We only have HTML truncation data, requested HTML body but only // have plaintext. $this->_options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_PLAIN] = $this->_options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_HTML]; } // Fetch the data from the IMAP client. $data = $this->_fetchData(array('html_id' => $html_id, 'text_id' => $text_id)); if (!empty($text_id) && $want_plain_text) { $this->_plain = $this->_getPlainPart($data, $text_body_part); } if (!empty($html_id) && $want_html_text) { $results = $this->_getHtmlPart($data, $html_body_part, $want_html_as_plain); $this->_html = !empty($results['html']) ? $results['html'] : null; $this->_plain = !empty($results['plain']) ? $results['plain'] : null; } if (!empty($this->_options['bodypartprefs'])) { $this->_bodyPart = $this->_getBodyPart($data, !empty($html_id) ? $html_body_part : $text_body_part, empty($html_id)); } $text_body_part = null; $html_body_part = null; }
/** * Returns the main text body of the message suitable for sending over * EAS response. * * @param array $options An options array containgin: * - bodyprefs: (array) Bodypref settings * DEFAULT: none (No bodyprefs used). * - 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 we are supporting. * DEFAULT 2.5 * * @return array An array of one or both of 'plain' and 'html' content. * * @throws Horde_ActiveSync_Exception, Horde_Exception_NotFound */ public function getMessageBodyData(array $options = array()) { $version = empty($options['protocolversion']) ? Horde_ActiveSync::VERSION_TWOFIVE : $options['protocolversion']; // Look for the parts we need. We try to detect and fetch only the parts // we need, while ensuring we have something to return. So, e.g., if we // don't have BODYPREF_TYPE_HTML, we only request plain text, but if we // can't find plain text but we have a html body, fetch that anyway. $text_id = $this->_message->findBody('plain'); $html_id = $this->_message->findBody('html'); // Deduce which part(s) we need to request. $want_html_text = $version >= Horde_ActiveSync::VERSION_TWELVE && (!empty($options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_HTML]) || !empty($options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_MIME])); $want_plain_text = $version == Horde_ActiveSync::VERSION_TWOFIVE || empty($options['bodyprefs']) || !empty($options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_PLAIN]) || !empty($options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_RTF]) || !empty($options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_MIME]) || $want_html_text && empty($html_id); $want_html_as_plain = false; if (!empty($text_id) && $want_plain_text) { $text_body_part = $this->_message->getPart($text_id); $charset = $text_body_part->getCharset(); } elseif ($want_plain_text && !empty($html_id) && empty($options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_MIME])) { $want_html_text = true; $want_html_as_plain = true; } if (!empty($html_id) && $want_html_text) { $html_body_part = $this->_message->getPart($html_id); $html_charset = $html_body_part->getCharset(); } // Sanity check the truncation stuff if (empty($options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_PLAIN]) && !empty($options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_HTML]) && $want_plain_text && $want_html_text) { // We only have HTML truncation data, requested HTML body but only // have plaintext. $options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_PLAIN] = $options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_HTML]; } $query = new Horde_Imap_Client_Fetch_Query(); $query_opts = array('decode' => true, 'peek' => true); // Get body information if ($version >= Horde_ActiveSync::VERSION_TWELVE) { if (!empty($html_id)) { $query->bodyPartSize($html_id); $query->bodyPart($html_id, $query_opts); } if (!empty($text_id)) { $query->bodyPart($text_id, $query_opts); $query->bodyPartSize($text_id); } } else { // EAS 2.5 Plaintext body $query->bodyPart($text_id, $query_opts); $query->bodyPartSize($text_id); } try { $fetch_ret = $this->_imap->fetch($this->_mbox, $query, array('ids' => new Horde_Imap_Client_Ids(array($this->_uid)))); } catch (Horde_Imap_Client_Exception $e) { throw new Horde_ActiveSync_Exception($e); } if (!($data = $fetch_ret->first())) { throw new Horde_Exception_NotFound(sprintf('Could not load message %s from server.', $this->_uid)); } $return = array(); if (!empty($text_id) && $want_plain_text) { $text = $data->getBodyPart($text_id); if (!$data->getBodyPartDecode($text_id)) { $text_body_part->setContents($text); $text = $text_body_part->getContents(); } $text_size = !is_null($data->getBodyPartSize($text_id)) ? $data->getBodyPartSize($text_id) : Horde_String::length($text); if (!empty($options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize'])) { // EAS >= 12.0 truncation $text = Horde_String::substr($text, 0, $options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize'], $charset); } $truncated = $text_size > Horde_String::length($text); if ($version >= Horde_ActiveSync::VERSION_TWELVE && $truncated && !empty($options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['allornone'])) { $text = ''; } $return['plain'] = array('charset' => $charset, 'body' => $text, 'truncated' => $truncated, 'size' => $text_size); } if (!empty($html_id) && $want_html_text) { $html = $data->getBodyPart($html_id); if (!$data->getBodyPartDecode($html_id)) { $html_body_part->setContents($html); $html = $html_body_part->getContents(); } // Size of the original HTML part. $html_size = !is_null($data->getBodyPartSize($html_id)) ? $data->getBodyPartSize($html_id) : Horde_String::length($html); if (!empty($options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_HTML]['truncationsize'])) { $html = Horde_String::substr($html, 0, $options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_HTML]['truncationsize'], $html_charset); } elseif ($want_html_as_plain) { $html = Horde_Text_Filter::filter($html, 'Html2text', array('charset' => $html_charset)); // Get the new size, since it probably changed. $html_size = Horde_String::length($html); if (!empty($options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize'])) { // EAS >= 12.0 truncation $html = Horde_String::substr($html, 0, $options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_PLAIN]['truncationsize'], $html_charset); } } // Was the part truncated? $truncated = $html_size > Horde_String::length($html); if ($want_html_as_plain) { $return['plain'] = array('charset' => $html_charset, 'body' => $html, 'truncated' => $truncated, 'size' => $html_size); } if ($version >= Horde_ActiveSync::VERSION_TWELVE && !($truncated && !empty($options['bodyprefs'][Horde_ActiveSync::BODYPREF_TYPE_HTML]['allornone']))) { $return['html'] = array('charset' => $html_charset, 'body' => $html, 'estimated_size' => $html_size, 'truncated' => $truncated); } } return $return; }