Horde_ActiveSync_Imap_Adapter::_buildMailMessage PHP Method

_buildMailMessage() protected method

Builds a proper AS mail message object.
protected _buildMailMessage ( Horde_Imap_Client_Mailbox $mbox, Horde_Imap_Client_Data_Fetch $data, array $options = [] ) : Horde_ActiveSync_Message_Mail
$mbox Horde_Imap_Client_Mailbox The IMAP mailbox.
$data Horde_Imap_Client_Data_Fetch The fetch results.
$options array 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;
        }
        // Fill in other header data
        try {
            $eas_message->from = Horde_ActiveSync_Utils::ensureUtf8($imap_message->getFromAddress(), 'UTF-8');
        } catch (Horde_ActiveSync_Exception $e) {
            $this->_logger->err($e->getMessage());
        }
        try {
            $eas_message->cc = Horde_ActiveSync_Utils::ensureUtf8($imap_message->getCc(), 'UTF-8');
        } catch (Horde_ActiveSync_Exception $e) {
            $this->_logger->err($e->getMessage());
        }
        try {
            $eas_message->reply_to = Horde_ActiveSync_Utils::ensureUtf8($imap_message->getReplyTo(), 'UTF-8');
        } catch (Horde_ActiveSync_Exception $e) {
            $this->_logger->err($e->getMessage());
        }
        // Ensure we don't send broken UTF8 data to the client. It makes clients
        // angry. And we don't like angry clients.
        $eas_message->subject = Horde_ActiveSync_Utils::ensureUtf8($imap_message->getSubject(), 'UTF-8');
        $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;
    }