Author: Michael J Rubinsky (mrubinsk@horde.org)
示例#1
0
 public function testBase64Uri()
 {
     /* Provision Request for version 12.1 */
     $url = 'eRQJBBCuTs0Z9ZK6Vldwb/dM8JusBHx8TOgDUFBD';
     $results = Horde_ActiveSync_Utils::decodeBase64($url);
     $fixture = array('ProtVer' => '12.1', 'Cmd' => 'Provision', 'Locale' => 1033, 'DeviceId' => 'ae4ecd19f592ba5657706ff74cf09bac', 'PolicyKey' => 3897326716.0, 'DeviceType' => 'PPC');
     $this->assertEquals($fixture, $results);
     /* Smart Forward */
     $url = 'eQIJBBCuTs0Z9ZK6Vldwb/dM8JusBHVeHIQDUFBDBwEBAwYxMTkyODEBBUlOQk9Y';
     $results = Horde_ActiveSync_Utils::decodeBase64($url);
     // This is binary data, test it separately.
     $fixture = array('ProtVer' => '12.1', 'Cmd' => 'SmartForward', 'Locale' => 1033, 'DeviceId' => 'ae4ecd19f592ba5657706ff74cf09bac', 'PolicyKey' => 2216451701.0, 'DeviceType' => 'PPC', 'ItemId' => '119281', 'CollectionId' => 'INBOX', 'AcceptMultiPart' => false, 'SaveInSent' => true);
     $this->assertEquals($fixture, $results);
 }
示例#2
0
文件: UtilsTest.php 项目: horde/horde
 public function testBase64Uri()
 {
     /* Settings Request for versions >= 12.1 */
     $url = 'oBEJBBBOaW5lMkVDN0VDMEJCNTREBAGJpmIHQW5kcm9pZAcBAA==';
     $results = Horde_ActiveSync_Utils::decodeBase64($url);
     $fixture = array('ProtVer' => '16.0', 'Cmd' => 'Settings', 'Locale' => 1033, 'DeviceId' => '4e696e65324543374543304242353444', 'PolicyKey' => 1655081217, 'DeviceType' => 'Android', 'SaveInSent' => false, 'AcceptMultiPart' => false);
     $this->assertEquals($fixture, $results);
     /* Smart Forward */
     $url = 'eQIJBBCuTs0Z9ZK6Vldwb/dM8JusBHVeHIQDUFBDBwEBAwYxMTkyODEBBUlOQk9Y';
     $results = Horde_ActiveSync_Utils::decodeBase64($url);
     $results['PolicyKey'] = sprintf('%u', $results['PolicyKey']);
     // This is binary data, test it separately.
     $fixture = array('ProtVer' => '12.1', 'Cmd' => 'SmartForward', 'Locale' => 1033, 'DeviceId' => 'ae4ecd19f592ba5657706ff74cf09bac', 'PolicyKey' => '2216451701', 'DeviceType' => 'PPC', 'ItemId' => '119281', 'CollectionId' => 'INBOX', 'AcceptMultiPart' => false, 'SaveInSent' => true);
     $this->assertEquals($fixture, $results);
 }
示例#3
0
 /**
  * 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;
 }
示例#4
0
 /**
  * Validate the body data to ensure consistent EOL and UTF8 data. Returns
  * body data in a stream object.
  *
  * @param array $data  The body data. @see self::_bodyPartText() for
  *                     structure.
  *
  * @return array  The validated body data array. @see self::_bodyPartText()
  */
 protected function _validateBodyData(&$data)
 {
     $stream = new Horde_Stream_Temp(array('max_memory' => 1048576));
     $filter_h = stream_filter_append($stream->stream, 'horde_eol', STREAM_FILTER_WRITE);
     $stream->add(Horde_ActiveSync_Utils::ensureUtf8($data['body'], $data['charset']), true);
     stream_filter_remove($filter_h);
     $data['body'] = $stream;
 }
示例#5
0
 /**
  * Return the GET variables passed from the device, decoding from
  * base64 if needed.
  *
  * @return array  A hash of get variables => values.
  */
 public function getGetVars()
 {
     if (!empty($this->_get)) {
         return $this->_get;
     }
     $results = array();
     $get = $this->_request->getGetVars();
     // Do we need to decode the request parameters?
     if (!isset($get['Cmd']) && !isset($get['DeviceId']) && !isset($get['DeviceType'])) {
         $serverVars = $this->_request->getServerVars();
         if (isset($serverVars['QUERY_STRING']) && strlen($serverVars['QUERY_STRING']) >= 10) {
             $results = Horde_ActiveSync_Utils::decodeBase64($serverVars['QUERY_STRING']);
             // Normalize values.
             switch ($results['DeviceType']) {
                 case 'PPC':
                     $results['DeviceType'] = 'PocketPC';
                     break;
                 case 'SP':
                     $results['DeviceType'] = 'SmartPhone';
                     break;
                 case 'WP':
                 case 'WP8':
                     $results['DeviceType'] = 'WindowsPhone';
                     break;
                 case 'android':
                 case 'android40':
                     $results['DeviceType'] = 'android';
             }
             $this->_get = $results;
         }
     } else {
         $this->_get = $get;
     }
     return $this->_get;
 }
示例#6
0
文件: Mail.php 项目: raz0rsdge/horde
 /**
  * Build the HTML part of a SMARTREPLY or SMARTFORWARD
  *
  * @param string $html_id                The MIME part id of the html part of
  *                                       $base_part.
  * @param Horde_Mime_Part $mime_message  The MIME part of the email to be
  *                                       sent.
  * @param array $body_data @see Horde_ActiveSync_Imap_Message::getMessageBodyData()
  * @param Horde_Mime_Part $base_part     The base MIME part of the source
  *                                       message for a SMART request.
  *
  * @return string  The plaintext part of the email message that is being sent.
  */
 protected function _getHtmlPart($html_id, $mime_message, $body_data, $base_part)
 {
     if (!($id = $mime_message->findBody('html'))) {
         $smart_text = self::text2html(Horde_ActiveSync_Utils::ensureUtf8($mime_message->getPart($mime_message->findBody('plain'))->getContents(), $mime_message->getCharset()));
     } else {
         $smart_text = Horde_ActiveSync_Utils::ensureUtf8($mime_message->getPart($id)->getContents(), $mime_message->getCharset());
     }
     if ($this->_forward) {
         return $smart_text . $this->_forwardText($body_data, $base_part->getPart($html_id), true);
     }
     return $smart_text . $this->_replyText($body_data, $base_part->getPart($html_id), true);
 }
示例#7
0
 /**
  * Converts and validates the message body data structure.
  *
  * @param array $data  Message body data structure.
  *
  * @return array  The message body data structure, with the [html][body] and
  *                [plain][body] data converted to UTF-8, EOL normalized and
  *                placed in a stream.
  */
 protected function _validateMessageBodyData($data)
 {
     //We will need the eol filter to work around PHP bug 65776.
     stream_filter_register('horde_eol', 'Horde_Stream_Filter_Eol');
     if (!empty($data['plain'])) {
         $stream = new Horde_Stream_Temp(array('max_memory' => 1048576));
         $filter_h = stream_filter_append($stream->stream, 'horde_eol', STREAM_FILTER_WRITE);
         $stream->add(Horde_ActiveSync_Utils::ensureUtf8($data['plain']['body'], $data['plain']['charset']), true);
         stream_filter_remove($filter_h);
         $data['plain']['body'] = $stream;
     }
     if (!empty($data['html'])) {
         $stream = new Horde_Stream_Temp(array('max_memory' => 1048576));
         $filter_h = stream_filter_append($stream->stream, 'horde_eol', STREAM_FILTER_WRITE);
         $stream->add(Horde_ActiveSync_Utils::ensureUtf8($data['html']['body'], $data['html']['charset']), true);
         stream_filter_remove($filter_h);
         $data['html']['body'] = $stream;
     }
     return $data;
 }
示例#8
0
文件: Message.php 项目: horde/horde
 /**
  * Return the To addresses from this message.
  *
  * @return array  An array containing arrays of 'to' and 'displayto'
  *                addresses. @since 2.37.1, ensures the text is UTF8.
  */
 public function getToAddresses()
 {
     $to = $this->envelope->to;
     $dtos = $tos = array();
     foreach ($to->raw_addresses as $e) {
         $tos[] = Horde_ActiveSync_Utils::ensureUtf8($e->bare_address, 'UTF-8');
         $dtos[] = Horde_ActiveSync_Utils::ensureUtf8($e->label, 'UTF-8');
     }
     return array('to' => $tos, 'displayto' => $dtos);
 }