Автор: Michael J Rubinsky (mrubinsk@horde.org)
Пример #1
0
 public function create(Horde_Injector $injector)
 {
     if (empty($GLOBALS['conf']['activesync']['logging']['level'])) {
         $level = Horde_ActiveSync_Wbxml::LOG_PROTOCOL;
     } else {
         $level = $GLOBALS['conf']['activesync']['logging']['level'];
     }
     $server = new Horde_ActiveSync($injector->getInstance('Horde_ActiveSyncBackend'), new Horde_ActiveSync_Wbxml_Decoder(fopen('php://input', 'r'), $level), new Horde_ActiveSync_Wbxml_Encoder(fopen('php://output', 'w+'), $level), $injector->getInstance('Horde_ActiveSyncState'), $injector->getInstance('Horde_Controller_Request'));
     $server->setSupportedVersion($GLOBALS['conf']['activesync']['version']);
     $server->setLogger(new Horde_Core_ActiveSync_Logger_Factory());
     if (!empty($GLOBALS['conf']['openssl']['cafile'])) {
         $server->setRootCertificatePath($GLOBALS['conf']['openssl']['cafile']);
     }
     return $server;
 }
Пример #2
0
 /**
  * Imports a folder deletion from the PIM
  *
  * @param string $uid     The folder uid
  * @param string $parent  The folder id of the parent folder.
  */
 public function importFolderDeletion($uid, $parent = Horde_ActiveSync::FOLDER_ROOT)
 {
     $collections = $this->_as->getCollectionsObject();
     $parent_sid = !empty($parent) ? $collections->getBackendIdForFolderUid($parent) : $parent;
     $folderid = $collections->getBackendIdForFolderUid($uid);
     $change = array();
     $change['id'] = $uid;
     $this->_as->driver->deleteFolder($folderid, $parent_sid);
     $this->_state->updateState(Horde_ActiveSync::CHANGE_TYPE_DELETE, $change, Horde_ActiveSync::CHANGE_ORIGIN_NA);
 }
Пример #3
0
 /**
  * Handle the request.
  *
  * @return boolean
  */
 public function handle()
 {
     $this->_logger->info(sprintf('[%s] Request being handled for device: %s, Supporting protocol version: %s, Using Horde_ActiveSync v%s', $this->_procid, $this->_device->id, $this->_device->version, Horde_ActiveSync::LIBRARY_VERSION));
     $this->_logger->info(sprintf('[%s] GET VARIABLES: %s', $this->_procid, print_r($this->_activeSync->getGetVars(), true)));
     try {
         return $this->_handle();
     } catch (Exception $e) {
         $this->_logger->err($e->getMessage());
         throw $e;
     }
 }
Пример #4
0
 /**
  * Return the username and password to use for authentication.
  *
  * @return array  The username in index 0 and password in index 1.
  */
 protected function _getCredentials()
 {
     $user = $pass = '';
     $serverVars = $this->_server->request->getServerVars();
     if (!empty($serverVars['PHP_AUTH_PW'])) {
         // Standard case, PHP was passed the needed authentication info.
         $user = $serverVars['PHP_AUTH_USER'];
         $pass = $serverVars['PHP_AUTH_PW'];
     } elseif (!empty($serverVars['HTTP_AUTHORIZATION']) || !empty($serverVars['REDIRECT_HTTP_AUTHORIZATION']) || !empty($serverVars['Authorization'])) {
         $authorization = !empty($serverVars['HTTP_AUTHORIZATION']) ? $serverVars['HTTP_AUTHORIZATION'] : (!empty($serverVars['REDIRECT_HTTP_AUTHORIZATION']) ? $serverVars['REDIRECT_HTTP_AUTHORIZATION'] : $serverVars['Authorization']);
         $hash = base64_decode(str_replace('Basic ', '', $authorization));
         if (strpos($hash, ':') !== false) {
             list($user, $pass) = explode(':', $hash, 2);
         }
     } else {
         // Might be using X509 certs, so won't have the Auth headers or a
         // password.
         $get = $this->_server->getGetVars();
         if (!empty($get['User'])) {
             $user = $get['User'];
         }
     }
     return array($user, $pass);
 }
Пример #5
0
 /**
  * @todo remove for H6 when driver methods always return EAS objects.
  */
 protected function _getOofObject($info)
 {
     $info = new Horde_Support_Array($info);
     $oof = Horde_ActiveSync::messageFactory('Oof');
     $oof->state = $info['oofstate'];
     $oof->starttime = new Horde_Date($info['starttime']);
     $oof->endtime = new Horde_Date($info['endtime']);
     $msg = Horde_ActiveSync::messageFactory('OofMessage');
     $msg->internal = '';
     $msg->enabled = $info['oofmsgs'][0]['enabled'];
     $msg->reply = $info['oofmsgs'][0]['replymessage'];
     $msg->bodytype = 'text';
     $oof->messages[] = $msg;
     return $oof;
 }
Пример #6
0
 /**
  * Helper method to handle incoming OPTIONS nodes.
  *
  * @param array $collection  The current collection array.
  */
 public function _parseSyncOptions(&$collection)
 {
     $options = array();
     $haveElement = false;
     // These can be sent in any order.
     while (1) {
         if ($this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_FILTERTYPE)) {
             $options['filtertype'] = $this->_decoder->getElementContent();
             $haveElement = true;
             if (!$this->_decoder->getElementEndTag()) {
                 $this->_statusCode = self::STATUS_PROTERROR;
                 $this->_handleError($collection);
                 exit;
             }
         }
         // EAS > 12.1 the Collection Class can be part of OPTIONS.
         if ($this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_FOLDERTYPE)) {
             $haveElement = true;
             $options['class'] = $this->_decoder->getElementContent();
             if (!$this->_decoder->getElementEndTag()) {
                 $this->_statusCode = self::STATUS_PROTERROR;
                 $this->_handleError($collection);
                 exit;
             }
         }
         if ($this->_decoder->getElementStartTag(Horde_ActiveSync::AIRSYNCBASE_BODYPREFERENCE)) {
             $this->_bodyPrefs($options);
         }
         if ($this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_CONFLICT)) {
             $haveElement = true;
             $options['conflict'] = $this->_decoder->getElementContent();
             if (!$this->_decoder->getElementEndTag()) {
                 $this->_statusCode = self::STATUS_PROTERROR;
                 $this->_handleError;
                 exit;
             }
         }
         if ($this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_MIMESUPPORT)) {
             $haveElement = true;
             $this->_mimeSupport($options);
         }
         // SYNC_MIMETRUNCATION is used when no SYNC_BODYPREFS element is sent.
         if ($this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_MIMETRUNCATION)) {
             $haveElement = true;
             $options['mimetruncation'] = Horde_ActiveSync::getMIMETruncSize($this->_decoder->getElementContent());
             if (!$this->_decoder->getElementEndTag()) {
                 $this->_statusCode = self::STATUS_PROTERROR;
                 $this->_handleError($collection);
                 exit;
             }
         }
         // SYNC_TRUNCATION only applies to the body of non-email collections
         // or the BODY element of an Email in EAS 2.5.
         if ($this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_TRUNCATION)) {
             $haveElement = true;
             $options['truncation'] = Horde_ActiveSync::getTruncSize($this->_decoder->getElementContent());
             if (!$this->_decoder->getElementEndTag()) {
                 $this->_statusCode = self::STATUS_PROTERROR;
                 $this->_handleError($collection);
                 exit;
             }
         }
         // @todo This seems to no longer be supported by the specs? Probably
         // a leftover from EAS 1 or 2.0. Remove in H6.
         if ($this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_RTFTRUNCATION)) {
             $haveElement = true;
             $options['rtftruncation'] = $this->_decoder->getElementContent();
             if (!$this->_decoder->getElementEndTag()) {
                 $this->_statusCode = self::STATUS_PROTERROR;
                 $this->_handleError($collection);
                 exit;
             }
         }
         if ($this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_MAXITEMS)) {
             $haveElement = true;
             $options['maxitems'] = $this->_decoder->getElementContent();
             if (!$this->_decoder->getElementEndTag()) {
                 $this->_statusCode = self::STATUS_PROTERROR;
                 $this->_handleError($collection);
                 exit;
             }
         }
         // EAS 14.1
         if ($this->_device->version >= Horde_ActiveSync::VERSION_FOURTEENONE) {
             if ($this->_decoder->getElementStartTag(Horde_ActiveSync::RM_SUPPORT)) {
                 $haveElement = true;
                 $this->_rightsManagement($options);
             }
             if ($this->_decoder->getElementStartTag(Horde_ActiveSync::AIRSYNCBASE_BODYPARTPREFERENCE)) {
                 $haveElement = true;
                 $this->_bodyPartPrefs($options);
             }
         }
         $e = $this->_decoder->peek();
         if ($e[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG) {
             $this->_decoder->getElementEndTag();
             break;
         } elseif (!$haveElement) {
             $depth = 0;
             while (1) {
                 $e = $this->_decoder->getElement();
                 if ($e === false) {
                     $this->_logger->err(sprintf('[%s] Unexpected end of stream.', $this->_procid));
                     $this->_statusCode = self::STATUS_PROTERROR;
                     $this->_handleError($collection);
                     exit;
                 } elseif ($e[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG) {
                     $depth = $this->_decoder->isEmptyElement($e) ? $depth : $depth + 1;
                 } elseif ($e[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG) {
                     $depth--;
                 }
                 if ($depth == 0) {
                     break;
                 }
             }
         }
     }
     // Default to no filter as per the specs.
     if (!isset($options['filtertype'])) {
         $options['filtertype'] = '0';
     }
     if (!empty($options['class']) && $options['class'] == 'SMS') {
         return;
     }
     $collection = array_merge($collection, $options);
 }
Пример #7
0
 /**
  * Obtain the ActiveSync protocol version requested by the client headers.
  *
  * @return string  The EAS version requested by the client.
  */
 public function getProtocolVersion()
 {
     if (!isset(self::$_version)) {
         self::$_version = $this->_request->getHeader('MS-ASProtocolVersion');
         if (empty(self::$_version)) {
             $get = $this->getGetVars();
             self::$_version = empty($get['ProtVer']) ? '1.0' : $get['ProtVer'];
         }
     }
     return self::$_version;
 }
Пример #8
0
 /**
  * Build an appropriate attachment object from the given mime part.
  *
  * @param integer $id                  The mime id for the part
  * @param Horde_Mime_Part  $mime_part  The mime part.
  * @param float $version               The EAS version.
  *
  * @return Horde_ActiveSync_Message_AirSyncBaseAttachment |
  *         Horde_ActiveSync_Message_Attachment
  */
 protected function _buildEasAttachmentFromMime($id, Horde_Mime_Part $mime_part, $version)
 {
     if ($version > Horde_ActiveSync::VERSION_TWOFIVE) {
         $atc = Horde_ActiveSync::messageFactory('AirSyncBaseAttachment');
         $atc->contentid = $mime_part->getContentId();
         $atc->isinline = $mime_part->getDisposition() == 'inline';
     } else {
         $atc = Horde_ActiveSync::messageFactory('Attachment');
         $atc->attoid = $mime_part->getContentId();
     }
     $atc->attsize = intval($mime_part->getBytes(true));
     $atc->attname = $this->_mbox . ':' . $this->uid . ':' . $id;
     $atc->displayname = Horde_String::convertCharset($this->getPartName($mime_part, true), $this->basePart->getHeaderCharset(), 'UTF-8', true);
     $atc->attmethod = in_array($mime_part->getType(), array('message/rfc822', 'message/disposition-notification')) ? Horde_ActiveSync_Message_AirSyncBaseAttachment::ATT_TYPE_EMBEDDED : Horde_ActiveSync_Message_AirSyncBaseAttachment::ATT_TYPE_NORMAL;
     return $atc;
 }
Пример #9
0
 /**
  * Sends the next change in the set to the client.
  *
  * @return boolean|Horde_Exception True if more changes can be sent false if
  *                                 all changes were sent, Horde_Exception if
  *                                 there was an error sending an item.
  */
 public function sendNextChange()
 {
     if (empty($this->_currentCollection)) {
         if ($this->_step < count($this->_changes)) {
             $change = $this->_changes[$this->_step];
             switch ($change['type']) {
                 case Horde_ActiveSync::CHANGE_TYPE_CHANGE:
                     // Folder add/change.
                     if ($folder = $this->_as->driver->getFolder($change['serverid'])) {
                         // @TODO BC HACK. Need to ensure we have a _serverid here.
                         // REMOVE IN H6.
                         if (empty($folder->_serverid)) {
                             $folder->_serverid = $folder->serverid;
                         }
                         $stat = $this->_as->driver->statFolder($change['id'], $folder->parentid, $folder->displayname, $folder->_serverid, $folder->type);
                         $this->folderChange($folder);
                     } else {
                         $this->_logger->err(sprintf('[%s] Error stating %s: ignoring.', $this->_procid, $change['id']));
                         $stat = array('id' => $change['id'], 'mod' => $change['id'], 0);
                     }
                     // Update the state.
                     $this->_as->state->updateState(Horde_ActiveSync::CHANGE_TYPE_FOLDERSYNC, $stat);
                     break;
                 case Horde_ActiveSync::CHANGE_TYPE_DELETE:
                     $this->folderDeletion($change['id']);
                     $this->_as->state->updateState(Horde_ActiveSync::CHANGE_TYPE_DELETE, $change);
                     break;
             }
             $this->_step++;
             return true;
         } else {
             return false;
         }
     } else {
         if ($this->_step < count($this->_changes)) {
             $change = $this->_changes[$this->_step];
             // Ignore this change, no UID value, keep trying until we get a
             // good entry or we run out of entries.
             while (empty($change['id']) && $this->_step < count($this->_changes) - 1) {
                 $this->_logger->err('Missing UID value for an entry in: ' . $this->_currentCollection['id']);
                 $this->_step++;
                 $change = $this->_changes[$this->_step];
             }
             // Actually export the change by calling the appropriate
             // method to output the correct wbxml for this change.
             if (empty($change['ignore'])) {
                 switch ($change['type']) {
                     case Horde_ActiveSync::CHANGE_TYPE_CHANGE:
                         try {
                             $message = $this->_as->driver->getMessage($this->_currentCollection['serverid'], $change['id'], $this->_currentCollection);
                             $message->flags = isset($change['flags']) ? $change['flags'] : 0;
                             $this->messageChange($change['id'], $message);
                         } catch (Horde_Exception_NotFound $e) {
                             $this->_logger->err(sprintf('[%s] Message gone or error reading message from server: %s', $this->_procid, $e->getMessage()));
                             $this->_as->state->updateState($change['type'], $change);
                             $this->_step++;
                             return $e;
                         } catch (Horde_ActiveSync_Exception $e) {
                             $this->_logger->err(sprintf('[%s] Unknown backend error skipping message: %s', $this->_procid, $e->getMessage()));
                             $this->_as->state->updateState($change['type'], $change);
                             $this->_step++;
                             return $e;
                         }
                         break;
                     case Horde_ActiveSync::CHANGE_TYPE_DELETE:
                         $this->messageDeletion($change['id']);
                         break;
                     case Horde_ActiveSync::CHANGE_TYPE_SOFTDELETE:
                         $this->messageDeletion($change['id'], true);
                         break;
                     case Horde_ActiveSync::CHANGE_TYPE_FLAGS:
                         // Read flag.
                         $message = Horde_ActiveSync::messageFactory('Mail');
                         $message->flags = Horde_ActiveSync::CHANGE_TYPE_CHANGE;
                         $message->read = isset($change['flags']['read']) ? $change['flags']['read'] : false;
                         // "Flagged" flag.
                         if (isset($change['flags']['flagged']) && $this->_as->device->version >= Horde_ActiveSync::VERSION_TWELVE) {
                             $flag = Horde_ActiveSync::messageFactory('Flag');
                             $flag->flagstatus = $change['flags']['flagged'] == 1 ? Horde_ActiveSync_Message_Flag::FLAG_STATUS_ACTIVE : Horde_ActiveSync_Message_Flag::FLAG_STATUS_CLEAR;
                             $message->flag = $flag;
                         }
                         // Categories
                         if (!empty($change['categories']) && $this->_as->device->version > Horde_ActiveSync::VERSION_TWELVEONE) {
                             $message->categories = $change['categories'];
                         }
                         // Verbs
                         if ($this->_as->device->version >= Horde_ActiveSync::VERSION_FOURTEEN) {
                             if (isset($change['flags'][Horde_ActiveSync::CHANGE_REPLY_STATE])) {
                                 $message->lastverbexecuted = Horde_ActiveSync_Message_Mail::VERB_REPLY_SENDER;
                                 $message->lastverbexecutiontime = new Horde_Date($change['flags'][Horde_ActiveSync::CHANGE_REPLY_STATE]);
                             } elseif (isset($change['flags'][Horde_ActiveSync::CHANGE_REPLYALL_STATE])) {
                                 $message->lastverbexecuted = Horde_ActiveSync_Message_Mail::VERB_REPLY_ALL;
                                 $message->lastverbexecutiontime = new Horde_Date($change['flags'][Horde_ActiveSync::CHANGE_REPLYALL_STATE]);
                             } elseif (isset($change['flags'][Horde_ActiveSync::CHANGE_FORWARD_STATE])) {
                                 $message->lastverbexecuted = Horde_ActiveSync_Message_Mail::VERB_FORWARD;
                                 $message->lastverbexecutiontime = new Horde_Date($change['flags'][Horde_ActiveSync::CHANGE_FORWARD_STATE]);
                             }
                         }
                         // Export it.
                         $this->messageChange($change['id'], $message);
                         break;
                     case Horde_ActiveSync::CHANGE_TYPE_MOVE:
                         $this->messageMove($change['id'], $change['parent']);
                         break;
                 }
             }
             // Update the state.
             $this->_as->state->updateState($change['type'], $change);
             $this->_step++;
             return true;
         } else {
             return false;
         }
     }
 }
Пример #10
0
 /**
  * Handle request
  *
  * @return boolean
  */
 protected function _handle()
 {
     $this->_logger->info(sprintf('[%s] Handling SEARCH command.', $this->_device->id));
     $search_status = self::SEARCH_STATUS_SUCCESS;
     $store_status = self::STORE_STATUS_SUCCESS;
     $this->_collections = $this->_activeSync->getCollectionsObject();
     if (!$this->_decoder->getElementStartTag(self::SEARCH_SEARCH) || !$this->_decoder->getElementStartTag(self::SEARCH_STORE) || !$this->_decoder->getElementStartTag(self::SEARCH_NAME)) {
         $search_status = self::SEARCH_STATUS_ERROR;
     }
     $search_name = $this->_decoder->getElementContent();
     if (!$this->_decoder->getElementEndTag()) {
         $search_status = self::SEARCH_STATUS_ERROR;
         $store_status = self::STORE_STATUS_PROTERR;
     }
     if (!$this->_decoder->getElementStartTag(self::SEARCH_QUERY)) {
         $search_status = self::SEARCH_STATUS_ERROR;
         $store_status = self::STORE_STATUS_PROTERR;
     }
     $search_query = array();
     switch (Horde_String::lower($search_name)) {
         case 'documentlibrary':
             $search_query['query'] = $this->_parseQuery();
             break;
         case 'mailbox':
             $search_query['query'] = $this->_parseQuery();
             break;
         case 'gal':
             $search_query['query'] = $this->_decoder->getElementContent();
     }
     if (!$this->_decoder->getElementEndTag()) {
         $search_status = self::SEARCH_STATUS_ERROR;
         $store_status = self::STORE_STATUS_PROTERR;
     }
     $mime = Horde_ActiveSync::MIME_SUPPORT_NONE;
     if ($this->_decoder->getElementStartTag(self::SEARCH_OPTIONS)) {
         $searchbodypreference = array();
         while (1) {
             if ($this->_decoder->getElementStartTag(self::SEARCH_RANGE)) {
                 $search_query['range'] = $this->_decoder->getElementContent();
                 if (!$this->_decoder->getElementEndTag()) {
                     $search_status = self::SEARCH_STATUS_ERROR;
                     $store_status = self::STORE_STATUS_PROTERR;
                 }
             }
             if ($this->_decoder->getElementStartTag(self::SEARCH_DEEPTRAVERSAL)) {
                 if (!($search_query['deeptraversal'] = $this->_decoder->getElementContent())) {
                     $search_query['deeptraversal'] = true;
                 } elseif (!$this->_decoder->getElementEndTag()) {
                     return false;
                 }
             }
             if ($this->_decoder->getElementStartTag(self::SEARCH_REBUILDRESULTS)) {
                 if (!($search_query['rebuildresults'] = $this->_decoder->getElementContent())) {
                     $search_query['rebuildresults'] = true;
                 } elseif (!$this->_decoder->getElementEndTag()) {
                     return false;
                 }
             }
             if ($this->_decoder->getElementStartTag(self::SEARCH_USERNAME)) {
                 if (!($search_query['username'] = $this->_decoder->getElementContent())) {
                     return false;
                 } elseif (!$this->_decoder->getElementEndTag()) {
                     return false;
                 }
             }
             if ($this->_decoder->getElementStartTag(self::SEARCH_PASSWORD)) {
                 if (!($search_query['password'] = $this->_decoder->getElementContent())) {
                     return false;
                 } else {
                     if (!$this->_decoder->getElementEndTag()) {
                         return false;
                     }
                 }
             }
             if ($this->_decoder->getElementStartTag(self::SEARCH_SCHEMA)) {
                 if (!($search_query['schema'] = $this->_decoder->getElementContent())) {
                     $search_query['schema'] = true;
                 } elseif (!$this->_decoder->getElementEndTag()) {
                     return false;
                 }
             }
             // 14.1 Only
             if ($this->_decoder->getElementStartTag(self::SEARCH_PICTURE)) {
                 $search_query[self::SEARCH_PICTURE] = true;
                 if ($this->_decoder->getElementStartTag(self::SEARCH_MAXSIZE)) {
                     $search_query[self::SEARCH_MAXSIZE] = $this->_decoder->getElementContent();
                     if (!$this->_decoder->getElementEndTag()) {
                         return false;
                     }
                 }
                 if ($this->_decoder->getElementStartTag(self::SEARCH_MAXPICTURES)) {
                     $search_query[self::SEARCH_MAXPICTURES] = $this->_decoder->getElementContent();
                     if (!$this->_decoder->getElementEndTag()) {
                         return false;
                     }
                 }
             }
             if ($this->_decoder->getElementStartTag(Horde_ActiveSync::AIRSYNCBASE_BODYPREFERENCE)) {
                 $this->_bodyPrefs($searchbodypreference);
                 $searchbodypreference = empty($searchbodypreference['bodyprefs']) ? array() : $searchbodypreference['bodyprefs'];
             }
             if ($this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_MIMESUPPORT)) {
                 $this->_mimeSupport($searchbodypreference);
             }
             // EAS 14.1
             if ($this->_device->version >= Horde_ActiveSync::VERSION_FOURTEENONE) {
                 $rm = array();
                 if ($this->_decoder->getElementStartTag(Horde_ActiveSync::RM_SUPPORT)) {
                     $this->_rightsManagement($rm);
                 }
                 if ($this->_decoder->getElementStartTag(Horde_ActiveSync::AIRSYNCBASE_BODYPARTPREFERENCE)) {
                     $this->_bodyPartPrefs($search_query);
                 }
             }
             $e = $this->_decoder->peek();
             if ($e[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG) {
                 $this->_decoder->getElementEndTag();
                 break;
             }
         }
     }
     if (!$this->_decoder->getElementEndTag()) {
         //store
         $search_status = self::SEARCH_STATUS_ERROR;
         $store_status = self::STORE_STATUS_PROTERR;
     }
     if (!$this->_decoder->getElementEndTag()) {
         //search
         $search_status = self::SEARCH_STATUS_ERROR;
         $store_status = self::STORE_STATUS_PROTERR;
     }
     $search_query['range'] = empty($search_query['range']) ? '0-99' : $search_query['range'];
     switch (Horde_String::lower($search_name)) {
         case 'mailbox':
             $search_query['rebuildresults'] = !empty($search_query['rebuildresults']);
             $search_query['deeptraversal'] = !empty($search_query['deeptraversal']);
             break;
     }
     // Get search results from backend
     $search_result = $this->_driver->getSearchResults($search_name, $search_query);
     // @TODO: Remove for H6. Total should be returned from the search call,
     // if it's not, do the best we can an use the count of results from
     // this page.
     if (empty($search_result['total'])) {
         $search_result['total'] = count($search_result['rows']);
     }
     /* Send output */
     $this->_encoder->startWBXML();
     $this->_encoder->startTag(self::SEARCH_SEARCH);
     $this->_encoder->startTag(self::SEARCH_STATUS);
     $this->_encoder->content($search_status);
     $this->_encoder->endTag();
     $this->_encoder->startTag(self::SEARCH_RESPONSE);
     $this->_encoder->startTag(self::SEARCH_STORE);
     $this->_encoder->startTag(self::SEARCH_STATUS);
     $this->_encoder->content($store_status);
     $this->_encoder->endTag();
     if (is_array($search_result['rows']) && !empty($search_result['rows'])) {
         $count = 0;
         foreach ($search_result['rows'] as $u) {
             $count++;
             switch (Horde_String::lower($search_name)) {
                 case 'documentlibrary':
                     $this->_encoder->startTag(self::SEARCH_RESULT);
                     $doc = Horde_ActiveSync::messageFactory('DocumentLibrary');
                     $doc->linkid = $u['linkid'];
                     $doc->displayname = $u['name'];
                     $doc->isfolder = $u['is_folder'] ? '1' : '0';
                     $doc->creationdate = $u['created'];
                     $doc->lastmodifieddate = $u['modified'];
                     $doc->ishidden = '0';
                     $doc->contentlength = $u['content-length'];
                     if (!empty($u['content-type'])) {
                         $doc->contenttype = $u['content-type'];
                     }
                     $this->_encoder->startTag(self::SEARCH_PROPERTIES);
                     $doc->encodeStream($this->_encoder);
                     $this->_encoder->endTag();
                     $this->_encoder->endTag();
                     continue;
                 case 'gal':
                     $this->_encoder->startTag(self::SEARCH_RESULT);
                     $this->_encoder->startTag(self::SEARCH_PROPERTIES);
                     $this->_encoder->startTag(Horde_ActiveSync::GAL_DISPLAYNAME);
                     $this->_encoder->content($u[Horde_ActiveSync::GAL_DISPLAYNAME]);
                     $this->_encoder->endTag();
                     $this->_encoder->startTag(Horde_ActiveSync::GAL_PHONE);
                     $this->_encoder->content($u[Horde_ActiveSync::GAL_PHONE]);
                     $this->_encoder->endTag();
                     $this->_encoder->startTag(Horde_ActiveSync::GAL_OFFICE);
                     $this->_encoder->content($u[Horde_ActiveSync::GAL_OFFICE]);
                     $this->_encoder->endTag();
                     $this->_encoder->startTag(Horde_ActiveSync::GAL_TITLE);
                     $this->_encoder->content($u[Horde_ActiveSync::GAL_TITLE]);
                     $this->_encoder->endTag();
                     $this->_encoder->startTag(Horde_ActiveSync::GAL_COMPANY);
                     $this->_encoder->content($u[Horde_ActiveSync::GAL_COMPANY]);
                     $this->_encoder->endTag();
                     $this->_encoder->startTag(Horde_ActiveSync::GAL_ALIAS);
                     $this->_encoder->content($u[Horde_ActiveSync::GAL_ALIAS]);
                     $this->_encoder->endTag();
                     $this->_encoder->startTag(Horde_ActiveSync::GAL_FIRSTNAME);
                     $this->_encoder->content($u[Horde_ActiveSync::GAL_FIRSTNAME]);
                     $this->_encoder->endTag();
                     $this->_encoder->startTag(Horde_ActiveSync::GAL_LASTNAME);
                     $this->_encoder->content($u[Horde_ActiveSync::GAL_LASTNAME]);
                     $this->_encoder->endTag();
                     $this->_encoder->startTag(Horde_ActiveSync::GAL_HOMEPHONE);
                     $this->_encoder->content($u[Horde_ActiveSync::GAL_HOMEPHONE]);
                     $this->_encoder->endTag();
                     $this->_encoder->startTag(Horde_ActiveSync::GAL_MOBILEPHONE);
                     $this->_encoder->content($u[Horde_ActiveSync::GAL_MOBILEPHONE]);
                     $this->_encoder->endTag();
                     $this->_encoder->startTag(Horde_ActiveSync::GAL_EMAILADDRESS);
                     $this->_encoder->content($u[Horde_ActiveSync::GAL_EMAILADDRESS]);
                     $this->_encoder->endTag();
                     if ($this->_device->version >= Horde_ActiveSync::VERSION_FOURTEENONE && !empty($u[Horde_ActiveSync::GAL_PICTURE])) {
                         $this->_encoder->startTag(Horde_ActiveSync::GAL_PICTURE);
                         $u[Horde_ActiveSync::GAL_PICTURE]->encodeStream($this->_encoder);
                         $this->_encoder->endTag();
                     }
                     $this->_encoder->endTag();
                     //properties
                     $this->_encoder->endTag();
                     //result
                     break;
                 case 'mailbox':
                     $this->_encoder->startTag(self::SEARCH_RESULT);
                     $this->_encoder->startTag(Horde_ActiveSync::SYNC_FOLDERTYPE);
                     $this->_encoder->content(Horde_ActiveSync::CLASS_EMAIL);
                     $this->_encoder->endTag();
                     $this->_encoder->startTag(self::SEARCH_LONGID);
                     $this->_encoder->content($u['uniqueid']);
                     $this->_encoder->endTag();
                     $this->_encoder->startTag(Horde_ActiveSync::SYNC_FOLDERID);
                     $this->_encoder->content($this->_collections->getFolderUidForBackendId($u['searchfolderid']));
                     $this->_encoder->endTag();
                     $this->_encoder->startTag(self::SEARCH_PROPERTIES);
                     $msg = $this->_driver->ItemOperationsFetchMailbox($u['uniqueid'], $searchbodypreference, $mime);
                     $msg->encodeStream($this->_encoder);
                     $this->_encoder->endTag();
                     //properties
                     $this->_encoder->endTag();
                     //result
             }
         }
         if (!empty($search_query['range'])) {
             $range = explode('-', $search_query['range']);
             // If total results are less than max range,
             // we have all results and must modify the returned range.
             if ($count < $range[1] - $range[0] + 1) {
                 $search_range = $range[0] . '-' . ($count - 1);
             } else {
                 $search_range = $search_query['range'];
             }
         }
         $this->_encoder->startTag(self::SEARCH_RANGE);
         $this->_encoder->content($search_range);
         $this->_encoder->endTag();
         $this->_encoder->startTag(self::SEARCH_TOTAL);
         $this->_encoder->content($search_result['total']);
         $this->_encoder->endTag();
     }
     $this->_encoder->endTag();
     //store
     $this->_encoder->endTag();
     //response
     $this->_encoder->endTag();
     //search
     return true;
 }
Пример #11
0
 /**
  * Handle incoming SYNC nodes
  *
  * @param array $collection  The current collection array.
  *
  * @return boolean
  */
 protected function _parseSyncCommands(&$collection)
 {
     // Some broken clients send SYNC_COMMANDS with a synckey of 0.
     // This is a violation of the spec, and could lead to all kinds
     // of data integrity issues.
     if (empty($collection['synckey'])) {
         $this->_logger->warn(sprintf('[%s] Attempting a SYNC_COMMANDS, but device failed to send synckey. Ignoring.', $this->_procid));
     }
     if (empty($collection['class'])) {
         $collection['class'] = $this->_collections->getCollectionClass($collection['id']);
     }
     if (empty($collection['serverid'])) {
         $collection['serverid'] = $this->_collections->getBackendIdForFolderUid($collection['id']);
     }
     try {
         $this->_collections->initCollectionState($collection);
     } catch (Horde_ActiveSync_Exception_StateGone $e) {
         $this->_logger->warn(sprintf('[%s] State not found sending STATUS_KEYMISM', $this->_procid));
         $this->_statusCode = self::STATUS_KEYMISM;
         $this->_handleError($collection);
         return false;
     } catch (Horde_ActiveSync_Exception_StaleState $e) {
         $this->_logger->notice($e->getMessage());
         $this->_statusCode = self::STATUS_SERVERERROR;
         $this->_handleGlobalSyncError();
         return false;
     } catch (Horde_ActiveSync_Exception $e) {
         $this->_logger->err($e->getMessage());
         $this->_statusCode = self::STATUS_SERVERERROR;
         $this->_handleGlobalSyncError();
         return false;
     }
     // Configure importer with last state
     if (!empty($collection['synckey'])) {
         $importer = $this->_activeSync->getImporter();
         $importer->init($this->_state, $collection['id'], $collection['conflict']);
     }
     $nchanges = 0;
     while (1) {
         // SYNC_MODIFY, SYNC_REMOVE, SYNC_ADD or SYNC_FETCH
         $element = $this->_decoder->getElement();
         if ($element[Horde_ActiveSync_Wbxml::EN_TYPE] != Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG) {
             $this->_decoder->_ungetElement($element);
             break;
         }
         $nchanges++;
         // Only sent during SYNC_MODIFY/SYNC_REMOVE/SYNC_FETCH
         if (($element[Horde_ActiveSync_Wbxml::EN_TAG] == Horde_ActiveSync::SYNC_MODIFY || $element[Horde_ActiveSync_Wbxml::EN_TAG] == Horde_ActiveSync::SYNC_REMOVE || $element[Horde_ActiveSync_Wbxml::EN_TAG] == Horde_ActiveSync::SYNC_FETCH) && $this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_SERVERENTRYID)) {
             $serverid = $this->_decoder->getElementContent();
             // Work around broken clients (Blackberry) that can send empty
             // $serverid values as a single empty <SYNC_SERVERENTRYID /> tag.
             if ($serverid !== false && !$this->_decoder->getElementEndTag()) {
                 $this->_statusCode = self::STATUS_PROTERROR;
                 $this->_handleGlobalSyncError();
                 $this->_logger->err('Parsing Error - expecting </SYNC_SERVERENTRYID>');
                 return false;
             }
         } else {
             $serverid = false;
         }
         // This tag is only sent here during > 12.1 and SYNC_ADD requests...
         // and it's not even sent by all clients. Parse it if it's there,
         // ignore it if not.
         if ($this->_activeSync->device->version > Horde_ActiveSync::VERSION_TWELVEONE && $element[Horde_ActiveSync_Wbxml::EN_TAG] == Horde_ActiveSync::SYNC_ADD && $this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_FOLDERTYPE)) {
             $collection['class'] = $this->_decoder->getElementContent();
             if (!$this->_decoder->getElementEndTag()) {
                 $this->_statusCode = self::STATUS_PROTERROR;
                 $this->_handleGlobalSyncError();
                 $this->_logger->err('Parsing Error - expecting </SYNC_FOLDERTYPE>');
                 return false;
             }
         }
         // Only sent during SYNC_ADD
         if ($element[Horde_ActiveSync_Wbxml::EN_TAG] == Horde_ActiveSync::SYNC_ADD && $this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_CLIENTENTRYID)) {
             $clientid = $this->_decoder->getElementContent();
             if (!$this->_decoder->getElementEndTag()) {
                 $this->_statusCode = self::STATUS_PROTERROR;
                 $this->_handleGlobalSyncError();
                 $this->_logger->err('Parsing Error - expecting </SYNC_CLIENTENTRYID>');
                 return false;
             }
         } else {
             $clientid = false;
         }
         // Create Message object from messages passed from PIM.
         // Only passed during SYNC_ADD or SYNC_MODIFY
         if (($element[Horde_ActiveSync_Wbxml::EN_TAG] == Horde_ActiveSync::SYNC_ADD || $element[Horde_ActiveSync_Wbxml::EN_TAG] == Horde_ActiveSync::SYNC_MODIFY) && $this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_DATA)) {
             switch ($collection['class']) {
                 case Horde_ActiveSync::CLASS_EMAIL:
                     $appdata = Horde_ActiveSync::messageFactory('Mail');
                     $appdata->decodeStream($this->_decoder);
                     break;
                 case Horde_ActiveSync::CLASS_CONTACTS:
                     $appdata = Horde_ActiveSync::messageFactory('Contact');
                     $appdata->decodeStream($this->_decoder);
                     break;
                 case Horde_ActiveSync::CLASS_CALENDAR:
                     $appdata = Horde_ActiveSync::messageFactory('Appointment');
                     $appdata->decodeStream($this->_decoder);
                     break;
                 case Horde_ActiveSync::CLASS_TASKS:
                     $appdata = Horde_ActiveSync::messageFactory('Task');
                     $appdata->decodeStream($this->_decoder);
                     break;
                 case Horde_ActiveSync::CLASS_NOTES:
                     $appdata = Horde_ActiveSync::messageFactory('Note');
                     $appdata->decodeStream($this->_decoder);
                     break;
                 case Horde_ActiveSync::CLASS_SMS:
                     $appdata = Horde_ActiveSync::messageFactory('Mail');
                     $appdata->decodeStream($this->_decoder);
                     break;
             }
             if (!$this->_decoder->getElementEndTag()) {
                 // End application data
                 $this->_statusCode = self::STATUS_PROTERROR;
                 $this->_handleGlobalSyncError();
                 return false;
             }
         }
         if (!empty($collection['synckey'])) {
             switch ($element[Horde_ActiveSync_Wbxml::EN_TAG]) {
                 case Horde_ActiveSync::SYNC_MODIFY:
                     if (isset($appdata)) {
                         $id = $importer->importMessageChange($serverid, $appdata, $this->_device, false);
                         if ($id && !is_array($id)) {
                             $collection['importedchanges'] = true;
                         } elseif (is_array($id)) {
                             $collection['importfailures'][$id[0]] = $id[1];
                         }
                     }
                     break;
                 case Horde_ActiveSync::SYNC_ADD:
                     if (isset($appdata)) {
                         $id = $importer->importMessageChange(false, $appdata, $this->_device, $clientid, $collection['class']);
                         if ($clientid && $id && !is_array($id)) {
                             $collection['clientids'][$clientid] = $id;
                             $collection['importedchanges'] = true;
                         } elseif (!$id || is_array($id)) {
                             $collection['clientids'][$clientid] = false;
                         }
                     }
                     break;
                 case Horde_ActiveSync::SYNC_REMOVE:
                     // Work around broken clients that send empty $serverid.
                     if ($serverid) {
                         $collection['removes'][] = $serverid;
                     }
                     break;
                 case Horde_ActiveSync::SYNC_FETCH:
                     $collection['fetchids'][] = $serverid;
                     break;
             }
         }
         if (!$this->_decoder->getElementEndTag()) {
             $this->_statusCode = self::STATUS_PROTERROR;
             $this->_handleGlobalSyncError();
             $this->_logger->err('Parsing error');
             return false;
         }
     }
     // Do all the SYNC_REMOVE requests at once
     if (!empty($collection['removes']) && !empty($collection['synckey'])) {
         if (!empty($collection['deletesasmoves']) && ($folderid = $this->_driver->getWasteBasket($collection['class']))) {
             $results = $importer->importMessageMove($collection['removes'], $folderid);
         } else {
             $results = $importer->importMessageDeletion($collection['removes'], $collection['class']);
             if (is_array($results)) {
                 $results['results'] = $results;
                 $results['missing'] = array_diff($collection['removes'], $results['results']);
             }
         }
         if (!empty($results['missing'])) {
             $collection['missing'] = $results['missing'];
         }
         unset($collection['removes']);
         $collection['importedchanges'] = true;
     }
     $this->_logger->info(sprintf('[%s] Processed %d incoming changes', $this->_procid, $nchanges));
     if (!$this->_decoder->getElementEndTag()) {
         // end commands
         $this->_statusCode = self::STATUS_PROTERROR;
         $this->_handleGlobalSyncError();
         $this->_logger->err('PARSING ERROR');
         return false;
     }
     return true;
 }
Пример #12
0
 /**
  * Handle the EAS 14.1 SETTINGS_DEVICEINFORMATION parsing.
  *
  * @return boolean|array  An array of received device information or false
  *                        on any protocol error.
  */
 protected function _handleSettings()
 {
     // EAS 14.1 REQUIRES SETTINGS_DEVICEINFORMATION in the PROVISION command.
     if (!$this->_decoder->getElementStartTag(Horde_ActiveSync_Request_Settings::SETTINGS_DEVICEINFORMATION)) {
         return false;
     }
     if (!$this->_decoder->getElementStartTag(Horde_ActiveSync_Request_Settings::SETTINGS_SET)) {
         return false;
     }
     $di = array();
     $settings = Horde_ActiveSync::messageFactory('DeviceInformation');
     $settings->decodeStream($this->_decoder);
     $di[Horde_ActiveSync_Request_Settings::SETTINGS_MODEL] = $settings->model;
     $di[Horde_ActiveSync_Request_Settings::SETTINGS_IMEI] = $settings->imei;
     $di[Horde_ActiveSync_Request_Settings::SETTINGS_FRIENDLYNAME] = $settings->friendlyname;
     $di[Horde_ActiveSync_Request_Settings::SETTINGS_OS] = $settings->os;
     $di[Horde_ActiveSync_Request_Settings::SETTINGS_OSLANGUAGE] = $settings->oslanguage;
     $di[Horde_ActiveSync_Request_Settings::SETTINGS_PHONENUMBER] = $settings->phonenumber;
     $di[Horde_ActiveSync_Request_Settings::SETTINGS_USERAGENT] = $settings->useragent;
     $di[Horde_ActiveSync_Request_Settings::SETTINGS_MOBILEOPERATOR] = $settings->mobileoperator;
     $di[Horde_ActiveSync_Request_Settings::SETTINGS_ENABLEOUTBOUNDSMS] = $settings->enableoutboundsms;
     $this->_decoder->getElementEndTag();
     $this->_decoder->getElementEndTag();
     return $di;
 }
Пример #13
0
 /**
  * Handle the request.
  *
  * @return boolean
  */
 protected function _handle()
 {
     // Be optimistic
     $this->_statusCode = self::STATUS_SUCCESS;
     $this->_logger->info(sprintf('[%s] Handling FOLDERSYNC command.', $this->_procid));
     // Check policy
     if (!$this->checkPolicyKey($this->_activeSync->getPolicyKey(), Horde_ActiveSync::FOLDERHIERARCHY_FOLDERSYNC)) {
         return true;
     }
     // Check global errors from pairing.
     if ($error = $this->_activeSync->checkGlobalError()) {
         $this->_statusCode = $error;
         $this->_handleError();
         return true;
     }
     // Start parsing input
     if (!$this->_decoder->getElementStartTag(Horde_ActiveSync::FOLDERHIERARCHY_FOLDERSYNC)) {
         $this->_logger->err('[Horde_ActiveSync::handleFolderSync] No input to parse');
         $this->_statusCode = self::STATUS_PROTOERR;
         $this->_handleError();
         return true;
     }
     // Get the current synckey from client
     if (!$this->_decoder->getElementStartTag(Horde_ActiveSync::FOLDERHIERARCHY_SYNCKEY)) {
         $this->_logger->err('[Horde_ActiveSync::handleFolderSync] No input to parse');
         $this->_statusCode = self::STATUS_PROTOERR;
         $this->_handleError();
         return true;
     }
     $synckey = $this->_decoder->getElementContent();
     if (!$this->_decoder->getElementEndTag()) {
         $this->_logger->err('[Horde_ActiveSync::handleFolderSync] No input to parse');
         $this->_statusCode = self::STATUS_PROTOERR;
         $this->_handleError();
         return true;
     }
     // Prepare the collections handler.
     $collections = $this->_activeSync->getCollectionsObject();
     try {
         $seenfolders = $collections->initHierarchySync($synckey);
     } catch (Horde_ActiveSync_Exception $e) {
         $this->_statusCode = self::STATUS_KEYMISM;
         $this->_handleError($e);
         return true;
     }
     // Track if we have changes or not
     $changes = false;
     // Deal with folder hierarchy changes
     if ($this->_decoder->getElementStartTag(Horde_ActiveSync::FOLDERHIERARCHY_CHANGES)) {
         // Ignore <Count> if present
         if ($this->_decoder->getElementStartTag(Horde_ActiveSync::FOLDERHIERARCHY_COUNT)) {
             $this->_decoder->getElementContent();
             if (!$this->_decoder->getElementEndTag()) {
                 $this->_statusCode = self::STATUS_PROTOERR;
                 $this->_handleError();
                 return true;
             }
         }
         // Process the incoming changes to folders
         $element = $this->_decoder->getElement();
         if ($element[Horde_ActiveSync_Wbxml::EN_TYPE] != Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG) {
             $this->_statusCode = self::STATUS_PROTOERR;
             $this->_handleError();
             return true;
         }
         // Configure importer with last state
         // @todo - integrate this with the collection manager.
         $importer = $this->_activeSync->getImporter();
         $importer->init($this->_state, false);
         while (1) {
             $folder = Horde_ActiveSync::messageFactory('Folder');
             if (!$folder->decodeStream($this->_decoder)) {
                 break;
             }
             switch ($element[Horde_ActiveSync_Wbxml::EN_TAG]) {
                 case SYNC_ADD:
                 case SYNC_MODIFY:
                     $new_server = $importer->importFolderChange($folder->serverid, $folder->displayname);
                     $serverid = $new_server->serverid;
                     if (!in_array($serverid, $seenfolders)) {
                         $seenfolders[] = $serverid;
                         $collections->updateFolderInHierarchy($folder);
                     } else {
                         $collections->updateFolderInHierarchy($folder, true);
                     }
                     $changes = true;
                     break;
                 case SYNC_REMOVE:
                     $importer->importFolderDeletion($folder->serverid);
                     if (($sid = array_search($folder->serverid, $seenfolders)) !== false) {
                         unset($seenfolders[$sid]);
                         $seenfolders = array_values($seenfolders);
                     }
                     $collections->deleteFolderFromHierarchy($folder->serverid);
                     $changes = true;
                     break;
             }
         }
         if (!$this->_decoder->getElementEndTag()) {
             $this->_statusCode = self::STATUS_PROTOERR;
             $this->_handleError();
             return true;
         }
     }
     if (!$this->_decoder->getElementEndTag()) {
         $this->_statusCode = self::STATUS_PROTOERR;
         $this->_handleError();
         return true;
     }
     // Start sending server -> client changes
     $newsynckey = $this->_state->getNewSyncKey($synckey);
     $exporter = new Horde_ActiveSync_Connector_Exporter($this->_activeSync);
     $exporter->setChanges($collections->getHierarchyChanges(), false);
     // Perform the actual sync operation
     while ($exporter->sendNextChange()) {
     }
     // Output our WBXML reply now
     $this->_encoder->StartWBXML();
     $this->_encoder->startTag(Horde_ActiveSync::FOLDERHIERARCHY_FOLDERSYNC);
     $this->_encoder->startTag(Horde_ActiveSync::FOLDERHIERARCHY_STATUS);
     $this->_encoder->content($this->_statusCode);
     $this->_encoder->endTag();
     $this->_encoder->startTag(Horde_ActiveSync::FOLDERHIERARCHY_SYNCKEY);
     $this->_encoder->content($changes || $exporter->count > 0 ? $newsynckey : $synckey);
     $this->_encoder->endTag();
     $this->_encoder->startTag(Horde_ActiveSync::FOLDERHIERARCHY_CHANGES);
     // Validate/clean up the hierarchy changes.
     $collections->validateHierarchyChanges($exporter, $seenfolders);
     $collections->updateHierarchyKey($changes || $exporter->count > 0 ? $newsynckey : $synckey);
     $this->_encoder->startTag(Horde_ActiveSync::FOLDERHIERARCHY_COUNT);
     $this->_encoder->content($exporter->count);
     $this->_encoder->endTag();
     if (count($exporter->changed) > 0) {
         foreach ($exporter->changed as $key => $folder) {
             if (isset($folder->serverid) && in_array($folder->serverid, $seenfolders)) {
                 $this->_encoder->startTag(self::UPDATE);
             } else {
                 $seenfolders[] = $folder->serverid;
                 $this->_encoder->startTag(self::ADD);
             }
             $folder->encodeStream($this->_encoder);
             $this->_encoder->endTag();
             $collections->updateFolderInHierarchy($folder);
         }
     }
     if (count($exporter->deleted) > 0) {
         foreach ($exporter->deleted as $folder_uid) {
             $this->_encoder->startTag(self::REMOVE);
             $this->_encoder->startTag(Horde_ActiveSync::FOLDERHIERARCHY_SERVERENTRYID);
             $this->_encoder->content($folder_uid);
             $this->_encoder->endTag();
             $this->_encoder->endTag();
             $collections->deleteFolderFromHierarchy($folder_uid);
         }
     }
     $this->_encoder->endTag();
     $this->_encoder->endTag();
     // Save state, clean
     if ($exporter->count) {
         $this->_state->setNewSyncKey($newsynckey);
         $this->_state->save();
     }
     $this->_cleanUpAfterPairing();
     $collections->save();
     return true;
 }
Пример #14
0
 /**
  * Handle EAS 14+ SendMail/SmartReply/SmartForward requests.
  *
  * @return boolean
  */
 protected function _handleWbxmlRequest()
 {
     $this->_logger->info(sprintf('[%s] Handling SENDMAIL command with Wbxml.', $this->_procid));
     // Get the first element and see what type of mail request we have.
     $e = $this->_decoder->getElement();
     if ($e[Horde_ActiveSync_Wbxml::EN_TYPE] != Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG) {
         $this->_handleError(Horde_ActiveSync_Status::INVALID_WBXML, Horde_ActiveSync_Message_SendMail::COMPOSEMAIL_SENDMAIL);
         return true;
     }
     $sendmail = $smartreply = $smartforward = false;
     switch ($e[Horde_ActiveSync_Wbxml::EN_TAG]) {
         case Horde_ActiveSync_Message_SendMail::COMPOSEMAIL_SENDMAIL:
             $sendmail = true;
             break;
         case Horde_ActiveSync_Message_SendMail::COMPOSEMAIL_SMARTREPLY:
             $smartreply = true;
             break;
         case Horde_ActiveSync_Message_SendMail::COMPOSEMAIL_SMARTFORWARD:
             $smartforward = true;
     }
     if (!$sendmail && !$smartreply && !$smartforward) {
         return $this->_handleError(Horde_ActiveSync_Status::INVALID_CONTENT, Horde_ActiveSync_Message_SendMail::COMPOSEMAIL_SENDMAIL);
     }
     $mail = Horde_ActiveSync::messageFactory('SendMail');
     $mail->decodeStream($this->_decoder);
     if ($smartreply || $smartforward) {
         $mail->source->folderid = $this->_activeSync->getCollectionsObject()->getBackendIdForFolderUid($mail->source->folderid);
     }
     try {
         // @TODO fix this ugly method call in H6 when we can break BC.
         return $this->_driver->sendMail(null, $smartforward, $smartreply, null, null, $mail);
     } catch (Horde_Exception_NotFound $ex) {
         $this->_logger->err($ex->getMessage());
         $this->_handleError(Horde_ActiveSync_Status::ITEM_NOT_FOUND, $e[Horde_ActiveSync_Wbxml::EN_TAG]);
     } catch (Horde_ActiveSync_Exception_EmailFatalFailure $ex) {
         $this->_logger->err($ex->getMessage());
         if ($this->_device->version < Horde_ActiveSync::VERSION_FOURTEEN) {
             // For now, return false, which causes a HTTP 500 to be returned
             // - the expected behavior prior to EAS 14.0. For 3.0, we will
             // rework this stuff into a Response object.
             $this->_logger->err(sprintf('[%s] Returning HTTP 500, since EAS version < 14.0', $this->_procid));
             return false;
         }
         $this->_handleError(Horde_ActiveSync_Status::SERVER_ERROR, $e[Horde_ActiveSync_Wbxml::EN_TAG]);
     } catch (Horde_ActiveSync_Exception $ex) {
         $this->_logger->err($ex->getMessage());
         if ($this->_device->version < Horde_ActiveSync::VERSION_FOURTEEN) {
             // For now, return false, which causes a HTTP 500 to be returned
             // - the expected behavior prior to EAS 14.0. For 3.0, we will
             // rework this stuff into a Response object.
             return false;
         }
         $this->_handleError(Horde_ActiveSync_Status::MAIL_SUBMISSION_FAILED, $e[Horde_ActiveSync_Wbxml::EN_TAG]);
     }
     return true;
 }
Пример #15
0
 /**
  * Set recurrence information for this task
  *
  * @param Horde_Date_Recurrence $recurrence
  */
 public function setRecurrence(Horde_Date_Recurrence $recurrence)
 {
     $r = Horde_ActiveSync::messageFactory('TaskRecurrence');
     // Map the type fields
     switch ($recurrence->recurType) {
         case Horde_Date_Recurrence::RECUR_DAILY:
             $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_DAILY;
             break;
         case Horde_Date_Recurrence::RECUR_WEEKLY:
             $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_WEEKLY;
             $r->dayofweek = $recurrence->getRecurOnDays();
             break;
         case Horde_Date_Recurrence::RECUR_MONTHLY_DATE:
             $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_MONTHLY;
             $r->dayofmonth = $recurrence->start->mday;
             break;
         case Horde_Date_Recurrence::RECUR_MONTHLY_WEEKDAY:
             $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_MONTHLY_NTH;
             $r->weekofmonth = ceil($recurrence->start->mday / 7);
             $r->dayofweek = $this->_dayOfWeekMap[$recurrence->start->dayOfWeek()];
             break;
         case Horde_Date_Recurrence::RECUR_YEARLY_DATE:
             $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_YEARLY;
             break;
         case Horde_Date_Recurrence::RECUR_YEARLY_WEEKDAY:
             $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_YEARLYNTH;
             $r->dayofweek = $this->_dayOfWeekMap[$recurrence->start->dayOfWeek()];
             $r->weekofmonth = ceil($recurrence->start->mday / 7);
             $r->monthofyear = $recurrence->start->month;
             break;
     }
     if (!empty($recurrence->recurInterval)) {
         $r->interval = $recurrence->recurInterval;
     }
     // AS messages can only have one or the other (or none), not both
     if ($recurrence->hasRecurCount()) {
         $r->occurrences = $recurrence->getRecurCount();
     } elseif ($recurrence->hasRecurEnd()) {
         $r->until = $recurrence->getRecurEnd();
     }
     // Set the start of the recurrence series.
     $r->start = clone $this->duedate;
     $this->_properties['recurrence'] = $r;
 }
Пример #16
0
 /**
  * Perform a search of the Global Address Book.
  *
  * @param array $query  A query array. @see self::getSearchResults()
  *
  * @return array  The results array. @see self::getSearchResults()
  */
 protected function _searchGal(array $query)
 {
     ob_start();
     $return = array('rows' => array(), 'status' => Horde_ActiveSync_Request_Search::STORE_STATUS_SUCCESS, 'total' => 0);
     try {
         $results = $this->_connector->contacts_search($query['query'], array('pictures' => !empty($query[Horde_ActiveSync_Request_Search::SEARCH_PICTURE])));
     } catch (Horde_ActiveSync_Exception $e) {
         $this->_logger->err($e);
         $this->_endBuffer();
         return $return;
     }
     // Honor range, and don't bother if no results
     $results = array_pop($results);
     $count = count($results);
     if (!$count) {
         $this->_endBuffer();
         return $return;
     }
     $return['total'] = $count;
     $this->_logger->info(sprintf("[%s] Horde_Core_ActiveSync_Driver::_searchGal() found %d matches.", $this->_pid, $count));
     if (!empty($query['range'])) {
         preg_match('/(.*)\\-(.*)/', $query['range'], $matches);
         $return_count = $matches[2] - $matches[1];
         $rows = array_slice($results, $matches[1], $return_count + 1, true);
     }
     $picture_count = 0;
     foreach ($rows as $row) {
         $entry = array(Horde_ActiveSync::GAL_ALIAS => !empty($row['alias']) ? $row['alias'] : '', Horde_ActiveSync::GAL_DISPLAYNAME => $row['name'], Horde_ActiveSync::GAL_EMAILADDRESS => !empty($row['email']) ? $row['email'] : '', Horde_ActiveSync::GAL_FIRSTNAME => $row['firstname'], Horde_ActiveSync::GAL_LASTNAME => $row['lastname'], Horde_ActiveSync::GAL_COMPANY => !empty($row['company']) ? $row['company'] : '', Horde_ActiveSync::GAL_HOMEPHONE => !empty($row['homePhone']) ? $row['homePhone'] : '', Horde_ActiveSync::GAL_PHONE => !empty($row['workPhone']) ? $row['workPhone'] : '', Horde_ActiveSync::GAL_MOBILEPHONE => !empty($row['cellPhone']) ? $row['cellPhone'] : '', Horde_ActiveSync::GAL_TITLE => !empty($row['title']) ? $row['title'] : '', Horde_ActiveSync::GAL_OFFICE => !empty($row['office']) ? $row['office'] : '');
         if (!empty($query[Horde_ActiveSync_Request_Search::SEARCH_PICTURE])) {
             $picture = Horde_ActiveSync::messageFactory('GalPicture');
             if (empty($row['photo'])) {
                 $picture->status = Horde_ActiveSync_Status::NO_PICTURE;
             } elseif (!empty($query[Horde_ActiveSync_Request_Search::SEARCH_MAXPICTURES]) && $picture_count > $query[Horde_ActiveSync_Request_Search::SEARCH_MAXPICTURES]) {
                 $picture->status = Horde_ActiveSync_Status::PICTURE_LIMIT_REACHED;
             } elseif (!empty($query[Horde_ActiveSync_Request_Search::SEARCH_MAXSIZE]) && strlen($row['photo']) > $query[Horde_ActiveSync_Request_Search::SEARCH_MAXSIZE]) {
                 $picture->status = Horde_ActiveSync_Status::PICTURE_TOO_LARGE;
             } else {
                 $picture->data = base64_encode($row['photo']['load']['data']);
                 $picture->status = Horde_ActiveSync_Status::PICTURE_SUCCESS;
                 ++$picture_count;
             }
             $entry[Horde_ActiveSync::GAL_PICTURE] = $picture;
         }
         $return['rows'][] = $entry;
     }
     $this->_endBuffer();
     return $return;
 }
Пример #17
0
 /**
  * Sends an RPC request to the server and returns the result.
  *
  * @param string $request  PHP input stream (ignored).
  */
 public function getResponse($request)
 {
     ob_start(null, 1048576);
     $serverVars = $this->_request->getServerVars();
     switch ($serverVars['REQUEST_METHOD']) {
         case 'OPTIONS':
         case 'GET':
             if ($serverVars['REQUEST_METHOD'] == 'GET' && $this->_get['Cmd'] != 'OPTIONS' && stripos($serverVars['REQUEST_URI'], 'autodiscover/autodiscover.xml') === false) {
                 $this->_logger->debug('Accessing ActiveSync endpoing from browser or missing required data.');
                 throw new Horde_Rpc_Exception(Horde_Rpc_Translation::t('Trying to access the ActiveSync endpoint from a browser. Not Supported.'));
             }
             if (stripos($serverVars['REQUEST_URI'], 'autodiscover/autodiscover.xml') !== false) {
                 try {
                     if (!$this->_server->handleRequest('Autodiscover', null)) {
                         $this->_logger->err('Unknown error during Autodiscover.');
                         throw new Horde_Exception('Unknown Error');
                     }
                 } catch (Horde_Exception_AuthenticationFailure $e) {
                     $this->_sendAuthenticationFailedHeaders();
                     exit;
                 } catch (Horde_Exception $e) {
                     $this->_handleError($e);
                 }
                 break;
             }
             $this->_logger->debug('Horde_Rpc_ActiveSync::getResponse() starting for OPTIONS');
             try {
                 if (!$this->_server->handleRequest('Options', null)) {
                     throw new Horde_Exception('Unknown Error');
                 }
             } catch (Horde_Exception_AuthenticationFailure $e) {
                 $this->_sendAuthenticationFailedHeaders();
                 exit;
             } catch (Horde_Exception $e) {
                 $this->_handleError($e);
             }
             break;
         case 'POST':
             // Autodiscover Request
             if (stripos($serverVars['REQUEST_URI'], 'autodiscover/autodiscover.xml') !== false) {
                 $this->_get['Cmd'] = 'Autodiscover';
                 $this->_get['DeviceId'] = null;
             }
             $this->_logger->debug('Horde_Rpc_ActiveSync::getResponse() starting for ' . $this->_get['Cmd']);
             try {
                 $ret = $this->_server->handleRequest($this->_get['Cmd'], $this->_get['DeviceId']);
                 if ($ret === false) {
                     throw new Horde_Rpc_Exception(sprintf('Received FALSE while handling %s command.', $this->_get['Cmd']));
                 } elseif ($ret !== true) {
                     $this->_contentType = $ret;
                 }
             } catch (Horde_ActiveSync_Exception_InvalidRequest $e) {
                 $this->_logger->err(sprintf('Returning HTTP 400 while handling %s command', $this->_get['Cmd']));
                 $this->_handleError($e);
                 header('HTTP/1.1 400 Invalid Request ' . $e->getMessage());
                 exit;
             } catch (Horde_Exception_AuthenticationFailure $e) {
                 $this->_sendAuthenticationFailedHeaders();
                 exit;
             } catch (Horde_Exception $e) {
                 $this->_logger->err(sprintf('Returning HTTP 500 while handling %s command.', $this->_get['Cmd']));
                 $this->_handleError($e);
                 header('HTTP/1.1 500 ' . $e->getMessage());
                 exit;
             }
             break;
     }
 }
Пример #18
0
 public function testDuplicateDetectionFromAsWithNoEmail()
 {
     $eas_obj = Horde_ActiveSync::messageFactory('Contact');
     $eas_obj->firstname = 'Firstname';
     $eas_obj->fileas = 'Firstname';
     $eas_obj->homephonenumber = '+55555555';
     $hash = self::$driver->fromASContact($eas_obj);
     self::$driver->add($hash);
     $result = self::$driver->search($hash);
     $this->assertEquals(1, count($result));
 }
Пример #19
0
 /**
  * Handle the request.
  *
  * @return string  The Content-Type of the attachment data.
  */
 protected function _handle()
 {
     $this->_logger->info(sprintf('[%s] Handling ITEMOPERATIONS command.', $this->_device->id));
     $this->_statusCode = self::STATUS_SUCCESS;
     if (!$this->_decoder->getElementStartTag(self::ITEMOPERATIONS_ITEMOPERATIONS)) {
         throw new Horde_ActiveSync_Exception('Protocol Error');
     }
     // The current itemoperation task
     $thisio = array();
     $mimesupport = 0;
     while (($reqtype = $this->_decoder->getElementStartTag(self::ITEMOPERATIONS_FETCH) ? self::ITEMOPERATIONS_FETCH : ($this->_decoder->getElementStartTag(self::ITEMOPERATIONS_EMPTYFOLDERCONTENT) ? self::ITEMOPERATIONS_EMPTYFOLDERCONTENT : -1)) != -1) {
         if ($reqtype == self::ITEMOPERATIONS_FETCH) {
             $thisio['type'] = 'fetch';
             while (($reqtag = $this->_decoder->getElementStartTag(self::ITEMOPERATIONS_STORE) ? self::ITEMOPERATIONS_STORE : ($this->_decoder->getElementStartTag(self::ITEMOPERATIONS_OPTIONS) ? self::ITEMOPERATIONS_OPTIONS : ($this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_SERVERENTRYID) ? Horde_ActiveSync::SYNC_SERVERENTRYID : ($this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_FOLDERID) ? Horde_ActiveSync::SYNC_FOLDERID : ($this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_DOCUMENTLIBRARY_LINKID) ? Horde_ActiveSync::SYNC_DOCUMENTLIBRARY_LINKID : ($this->_decoder->getElementStartTag(Horde_ActiveSync::AIRSYNCBASE_FILEREFERENCE) ? Horde_ActiveSync::AIRSYNCBASE_FILEREFERENCE : ($this->_decoder->getElementStartTag(Horde_ActiveSync_Request_Search::SEARCH_LONGID) ? Horde_ActiveSync_Request_Search::SEARCH_LONGID : -1))))))) != -1) {
                 if ($reqtag == self::ITEMOPERATIONS_OPTIONS) {
                     while (($thisoption = $this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_MIMESUPPORT) ? Horde_ActiveSync::SYNC_MIMESUPPORT : ($this->_decoder->getElementStartTag(Horde_ActiveSync::AIRSYNCBASE_BODYPREFERENCE) ? Horde_ActiveSync::AIRSYNCBASE_BODYPREFERENCE : ($this->_decoder->getElementStartTag(Horde_ActiveSync::AIRSYNCBASE_BODYPARTPREFERENCE) ? Horde_ActiveSync::AIRSYNCBASE_BODYPARTPREFERENCE : ($this->_decoder->getElementStartTag(self::ITEMOPERATIONS_SCHEMA) ? self::ITEMOPERATIONS_SCHEMA : ($this->_decoder->getElementStartTag(self::ITEMOPERATIONS_RANGE) ? self::ITEMOPERATIONS_RANGE : ($this->_decoder->getElementStartTag(self::ITEMOPERATIONS_USERNAME) ? self::ITEMOPERATIONS_USERNAME : ($this->_decoder->getElementStartTag(self::ITEMOPERATIONS_PASSWORD) ? self::ITEMOPERATIONS_PASSWORD : ($this->_decoder->getElementStartTag(Horde_ActiveSync::RM_SUPPORT) ? Horde_ActiveSync::RM_SUPPORT : -1)))))))) != -1) {
                         switch ($thisoption) {
                             case Horde_ActiveSync::SYNC_MIMESUPPORT:
                                 $mimesupport = $this->_decoder->getElementContent();
                                 $this->_decoder->getElementEndTag();
                                 break;
                             case Horde_ActiveSync::AIRSYNCBASE_BODYPREFERENCE:
                                 $this->_bodyPrefs($thisio);
                                 break;
                             case Horde_ActiveSync::AIRSYNCBASE_BODYPARTPREFERENCE:
                                 $this->_bodyPartPrefs($thisio);
                                 break;
                             case Horde_ActiveSync::RM_SUPPORT:
                                 $this->_rightsManagement($thisio);
                                 break;
                             case self::ITEMOPERATIONS_PASSWORD:
                                 $thisio['password'] = $this->_decoder->getElementContent();
                                 break;
                             case self::ITEMOPERATIONS_USERNAME:
                                 $thisio['username'] = $this->_decoder->getElementContent();
                                 break;
                             case self::ITEMOPERATIONS_RANGE:
                                 $thisio['range'] = $this->_decoder->getElementContent();
                                 break;
                             case self::ITEMOPERATIONS_SCHEMA:
                                 while (1) {
                                     $el = $this->_decoder->getElement();
                                     $e = $this->_decoder->peek();
                                     if ($e[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG) {
                                         $this->_decoder->getElementEndTag();
                                         break;
                                     }
                                 }
                         }
                     }
                 } elseif ($reqtag == self::ITEMOPERATIONS_STORE) {
                     $thisio['store'] = $this->_decoder->getElementContent();
                 } elseif ($reqtag == Horde_ActiveSync_Request_Search::SEARCH_LONGID) {
                     $thisio['searchlongid'] = $this->_decoder->getElementContent();
                 } elseif ($reqtag == Horde_ActiveSync::AIRSYNCBASE_FILEREFERENCE) {
                     $thisio['airsyncbasefilereference'] = $this->_decoder->getElementContent();
                 } elseif ($reqtag == Horde_ActiveSync::SYNC_SERVERENTRYID) {
                     $thisio['serverentryid'] = $this->_decoder->getElementContent();
                 } elseif ($reqtag == Horde_ActiveSync::SYNC_FOLDERID) {
                     $thisio['folderid'] = $this->_decoder->getElementContent();
                 } elseif ($reqtag == Horde_ActiveSync::SYNC_DOCUMENTLIBRARY_LINKID) {
                     $thisio['documentlibrarylinkid'] = $this->_decoder->getElementContent();
                 }
                 $e = $this->_decoder->peek();
                 if ($e[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG) {
                     $this->_decoder->getElementEndTag();
                 }
             }
             $itemoperations[] = $thisio;
             $this->_decoder->getElementEndTag();
             // end SYNC_ITEMOPERATIONS_FETCH
         } elseif ($reqtype == self::ITEMOPERATIONS_EMPTYFOLDERCONTENT) {
             $thisio['type'] = 'empty';
             while (($tag = $this->_decoder->getElementStartTag(Horde_ActiveSync::SYNC_FOLDERID) ? Horde_ActiveSync::SYNC_FOLDERID : ($this->_decoder->getElementStartTag(self::ITEMOPERATIONS_OPTIONS) ? self::ITEMOPERATIONS_OPTIONS : -1)) != -1) {
                 if ($tag == Horde_ActiveSync::SYNC_FOLDERID) {
                     $thisio['folderid'] = $this->_decoder->getElementContent();
                 } elseif ($tag == self::ITEMOPERATIONS_OPTIONS) {
                     $this->_decoder->getElementStartTag(self::ITEMOPERATIONS_DELETESUBFOLDERS);
                     $thisio['delete_subfolders'] = $this->_decoder->getElementContent();
                     $this->_decoder->getElementEndTag();
                 }
                 $this->_decoder->getElementEndTag();
             }
             $this->_decoder->getElementEndTag();
             // SYNC_ITEMSOPERATIONS_EMPTYFOLDERCONTENT
             $itemoperations[] = $thisio;
         }
     }
     $this->_decoder->getElementEndTag();
     // end SYNC_ITEMOPERATIONS_ITEMOPERATIONS
     $this->_encoder->startWBXML($this->_activeSync->multipart);
     $this->_encoder->startTag(self::ITEMOPERATIONS_ITEMOPERATIONS);
     $this->_encoder->startTag(self::ITEMOPERATIONS_STATUS);
     $this->_encoder->content(self::STATUS_SUCCESS);
     $this->_encoder->endTag();
     $this->_encoder->startTag(self::ITEMOPERATIONS_RESPONSE);
     $collections = $this->_activeSync->getCollectionsObject();
     foreach ($itemoperations as $value) {
         switch ($value['type']) {
             case 'fetch':
                 switch (Horde_String::lower($value['store'])) {
                     case 'mailbox':
                         // Yes, even though this is a "mailbox" store, this is
                         // how EAS identifies calendar attachments too since
                         // they are not documentLibrary items. The backend
                         // needs to be able to identify where to get the
                         // item from based solely on the filereference.
                         $this->_encoder->startTag(self::ITEMOPERATIONS_FETCH);
                         if (isset($value['airsyncbasefilereference'])) {
                             // filereference is already in the backend serverid format
                             // since it is taken from the AIRSYNCBASE_FILEREFERENCE
                             try {
                                 $msg = $this->_driver->itemOperationsGetAttachmentData($value['airsyncbasefilereference']);
                             } catch (Horde_ActiveSync_Exception $e) {
                                 $this->_statusCode = self::STATUS_ATTINVALID;
                             }
                             if (!$this->_encoder->multipart) {
                                 $msg->total = $this->_getDataSize($msg->data);
                                 $msg->range = '0-' . ($msg->total - 1);
                             }
                             $this->_outputStatus();
                             $this->_encoder->startTag(Horde_ActiveSync::AIRSYNCBASE_FILEREFERENCE);
                             $this->_encoder->content($value['airsyncbasefilereference']);
                             $this->_encoder->endTag();
                         } elseif (isset($value['searchlongid'])) {
                             $this->_outputStatus();
                             $this->_encoder->startTag(Horde_ActiveSync_Request_Search::SEARCH_LONGID);
                             $this->_encoder->content($value['searchlongid']);
                             $this->_encoder->endTag();
                             $this->_encoder->startTag(Horde_ActiveSync::SYNC_FOLDERTYPE);
                             $this->_encoder->content('Email');
                             $this->_encoder->endTag();
                             $msg = $this->_driver->itemOperationsFetchMailbox($value['searchlongid'], $value['bodyprefs'], $mimesupport);
                         } else {
                             $this->_outputStatus();
                             if (isset($value['folderid']) && isset($value['serverentryid'])) {
                                 $this->_encoder->startTag(Horde_ActiveSync::SYNC_FOLDERID);
                                 $this->_encoder->content($value['folderid']);
                                 $this->_encoder->endTag();
                                 $this->_encoder->startTag(Horde_ActiveSync::SYNC_SERVERENTRYID);
                                 $this->_encoder->content($value['serverentryid']);
                                 $this->_encoder->endTag();
                                 $this->_encoder->startTag(Horde_ActiveSync::SYNC_FOLDERTYPE);
                                 $this->_encoder->content('Email');
                                 $this->_encoder->endTag();
                                 $mailbox = $collections->getBackendIdForFolderUid($value['folderid']);
                                 $msg = $this->_driver->fetch($mailbox, $value['serverentryid'], array('bodyprefs' => $value['bodyprefs'], 'mimesupport' => $mimesupport));
                             }
                         }
                         if ($this->_statusCode == self::STATUS_SUCCESS) {
                             $this->_encoder->startTag(self::ITEMOPERATIONS_PROPERTIES);
                             $msg->encodeStream($this->_encoder);
                             $this->_encoder->endTag();
                         }
                         $this->_encoder->endTag();
                         break;
                     case 'documentlibrary':
                         $this->_encoder->startTag(self::ITEMOPERATIONS_FETCH);
                         try {
                             $u = $this->_driver->itemOperationsGetDocumentLibraryLink($value['documentlibrarylinkid'], array());
                             $doc = Horde_ActiveSync::messageFactory('Document');
                             $doc->range = '0-' . ($u['content-length'] - 1);
                             $doc->total = $u['content-length'];
                             $doc->data = $u['data']->stream;
                             $doc->version = $u['modified'];
                         } catch (Horde_ActiveSync_Exception $e) {
                             $this->_status = self::STATUS_NOT_SUPPORTED;
                         }
                         $this->_outputStatus();
                         $this->_encoder->startTag(Horde_ActiveSync::SYNC_DOCUMENTLIBRARY_LINKID);
                         $this->_encoder->content($u['linkid']);
                         $this->_encoder->endTag();
                         $this->_encoder->startTag(self::ITEMOPERATIONS_PROPERTIES);
                         $doc->encodeStream($this->_encoder);
                         $this->_encoder->endTag();
                         $this->_encoder->endTag();
                         break;
                     default:
                         $this->_logger->warn(sprintf('[%s] %s not supported by HANDLEITEMOPERATIONS.', $this->_device->id, $value['type']));
                         break;
                 }
                 break;
             case 'empty':
                 // @todo remove check for H6.
                 if (method_exists($this->_driver, 'itemOperationsEmptyFolder')) {
                     $map = array_flip($this->_state->getFolderUidToBackendIdMap());
                     $value['folderid'] = $map[$value['folderid']];
                     $this->_logger->info(sprintf('[%s] Handling EMPTYFOLDERCONTENT for collection %s.', $this->_device->id, $value['folderid']));
                     try {
                         $this->_driver->itemOperationsEmptyFolder($value);
                     } catch (Horde_ActiveSync_Exception $e) {
                         $this->_status = self::STATUS_NOT_SUPPORTED;
                     }
                 } else {
                     $this->_logger->err(sprintf('[%s] EMPTYFOLDERCONTENT not supported by driver.', $this->_device->id, $value['type']));
                 }
                 break;
             default:
                 $this->_logger->err(sprintf('[%s] %s not supported by HANDLEITEMOPERATIONS.', $this->_device->id, $value['type']));
                 break;
         }
     }
     $this->_encoder->endTag();
     //end SYNC_ITEMOPERATIONS_RESPONSE
     $this->_encoder->endTag();
     //end SYNC_ITEMOPERATIONS_ITEMOPERATIONS
     // @TODO This is for BC, remove in H6.
     return $this->_encoder->multipart ? 'application/vnd.ms-sync.multipart' : 'application/vnd.ms-sync.wbxml';
 }
Пример #20
0
 /**
  * Set recurrence information for this appointment
  *
  * @param Horde_Date_Recurrence $recurrence  The recurrence data.
  * @param integer $fdow                      The first day of the week.
  *        (A Horde_ActiveSync_Message_Recurrence:: constant).  @since 2.4.0
  */
 public function setRecurrence(Horde_Date_Recurrence $recurrence, $fdow = null)
 {
     $r = Horde_ActiveSync::messageFactory('Recurrence');
     if ($this->_version >= Horde_ActiveSync::VERSION_FOURTEENONE) {
         $r->firstdayofweek = $fdow;
     }
     /* Map the type fields */
     switch ($recurrence->recurType) {
         case Horde_Date_Recurrence::RECUR_DAILY:
             $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_DAILY;
             break;
         case Horde_Date_Recurrence::RECUR_WEEKLY:
             $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_WEEKLY;
             $r->dayofweek = $recurrence->getRecurOnDays();
             break;
         case Horde_Date_Recurrence::RECUR_MONTHLY_DATE:
             $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_MONTHLY;
             break;
         case Horde_Date_Recurrence::RECUR_MONTHLY_WEEKDAY:
             $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_MONTHLY_NTH;
             $r->weekofmonth = ceil($recurrence->start->mday / 7);
             $r->dayofweek = $this->_dayOfWeekMap[$recurrence->start->dayOfWeek()];
             break;
         case Horde_Date_Recurrence::RECUR_YEARLY_DATE:
             $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_YEARLY;
             $r->monthofyear = $recurrence->start->month;
             $r->dayofmonth = $recurrence->start->mday;
             break;
         case Horde_Date_Recurrence::RECUR_YEARLY_WEEKDAY:
             $r->type = Horde_ActiveSync_Message_Recurrence::TYPE_YEARLYNTH;
             $r->dayofweek = $this->_dayOfWeekMap[$recurrence->start->dayOfWeek()];
             $r->weekofmonth = ceil($recurrence->start->mday / 7);
             $r->monthofyear = $recurrence->start->month;
             break;
     }
     if (!empty($recurrence->recurInterval)) {
         $r->interval = $recurrence->recurInterval;
     }
     /* AS messages can only have one or the other (or none), not both */
     if ($recurrence->hasRecurCount()) {
         $r->occurrences = $recurrence->getRecurCount();
     } elseif ($recurrence->hasRecurEnd()) {
         $r->until = $recurrence->getRecurEnd();
     }
     // We don't support non-gregorian calendars.
     if ($this->_version >= Horde_ActiveSync::VERSION_FOURTEEN) {
         $r->calendartype = Horde_ActiveSync_Message_Recurrence::CALENDAR_TYPE_GREGORIAN;
     }
     $this->_properties['recurrence'] = $r;
 }
Пример #21
0
 /**
  * Sends the next message change to the client.
  *
  * @return @see self::sendNextChange()
  */
 protected function _sendNextChange()
 {
     if ($this->_step >= count($this->_changes)) {
         return false;
     }
     // Get the next change.
     $change = $this->_getNextChange();
     // Ignore entries with no UID.
     while (empty($change['id']) && $this->_step < count($this->_changes) - 1) {
         $this->_logger->notice(sprintf('Missing UID value for an entry in: %s. Details: %s.', $this->_currentCollection['id'], print_r($change, true)));
         $this->_step++;
         $change = $this->_getNextChange();
     }
     // Actually export the change by calling the appropriate
     // method to output the correct wbxml for this change.
     if (empty($change['ignore'])) {
         switch ($change['type']) {
             case Horde_ActiveSync::CHANGE_TYPE_CHANGE:
             case Horde_ActiveSync::CHANGE_TYPE_DRAFT:
                 try {
                     $message = $this->_as->driver->getMessage($this->_currentCollection['serverid'], $change['id'], $this->_currentCollection);
                     $message->flags = isset($change['flags']) ? $change['flags'] : false;
                     $this->messageChange($change['id'], $message);
                 } catch (Horde_Exception_NotFound $e) {
                     $this->_logger->notice(sprintf('[%s] Message gone or error reading message from server: %s', $this->_procid, $e->getMessage()));
                     $this->_as->state->updateState($change['type'], $change);
                     $this->_step++;
                     return $e;
                 } catch (Horde_ActiveSync_Exception $e) {
                     $this->_logger->err(sprintf('[%s] Unknown backend error skipping message: %s', $this->_procid, $e->getMessage()));
                     $this->_as->state->updateState($change['type'], $change);
                     $this->_step++;
                     return $e;
                 }
                 break;
             case Horde_ActiveSync::CHANGE_TYPE_DELETE:
                 $this->messageDeletion($change['id']);
                 break;
             case Horde_ActiveSync::CHANGE_TYPE_SOFTDELETE:
                 $this->messageDeletion($change['id'], true);
                 break;
             case Horde_ActiveSync::CHANGE_TYPE_FLAGS:
                 // Read flag.
                 $message = Horde_ActiveSync::messageFactory('Mail');
                 $message->flags = Horde_ActiveSync::CHANGE_TYPE_CHANGE;
                 $message->read = isset($change['flags']['read']) ? $change['flags']['read'] : false;
                 // "Flagged" flag.
                 if (isset($change['flags']['flagged']) && $this->_as->device->version >= Horde_ActiveSync::VERSION_TWELVE) {
                     $flag = Horde_ActiveSync::messageFactory('Flag');
                     $flag->flagstatus = $change['flags']['flagged'] == 1 ? Horde_ActiveSync_Message_Flag::FLAG_STATUS_ACTIVE : Horde_ActiveSync_Message_Flag::FLAG_STATUS_CLEAR;
                     $message->flag = $flag;
                 }
                 // Categories
                 if (!empty($change['categories']) && $this->_as->device->version > Horde_ActiveSync::VERSION_TWELVEONE) {
                     $message->categories = $change['categories'];
                 }
                 // Verbs
                 if ($this->_as->device->version >= Horde_ActiveSync::VERSION_FOURTEEN) {
                     if (isset($change['flags'][Horde_ActiveSync::CHANGE_REPLY_STATE])) {
                         $message->lastverbexecuted = Horde_ActiveSync_Message_Mail::VERB_REPLY_SENDER;
                         $message->lastverbexecutiontime = new Horde_Date($change['flags'][Horde_ActiveSync::CHANGE_REPLY_STATE]);
                     } elseif (isset($change['flags'][Horde_ActiveSync::CHANGE_REPLYALL_STATE])) {
                         $message->lastverbexecuted = Horde_ActiveSync_Message_Mail::VERB_REPLY_ALL;
                         $message->lastverbexecutiontime = new Horde_Date($change['flags'][Horde_ActiveSync::CHANGE_REPLYALL_STATE]);
                     } elseif (isset($change['flags'][Horde_ActiveSync::CHANGE_FORWARD_STATE])) {
                         $message->lastverbexecuted = Horde_ActiveSync_Message_Mail::VERB_FORWARD;
                         $message->lastverbexecutiontime = new Horde_Date($change['flags'][Horde_ActiveSync::CHANGE_FORWARD_STATE]);
                     }
                 }
                 // Export it.
                 $this->messageChange($change['id'], $message);
                 break;
             case Horde_ActiveSync::CHANGE_TYPE_MOVE:
                 $this->messageMove($change['id'], $change['parent']);
                 break;
         }
     }
     // Update the state.
     $this->_as->state->updateState($change['type'], $change);
     $this->_step++;
     return true;
 }
Пример #22
0
 /**
  * Perform a search of the Global Address Book.
  *
  * @param array $query  A query array. @see self::getSearchResults()
  *
  * @return array  The results array. @see self::getSearchResults()
  */
 protected function _searchGal(array $query)
 {
     ob_start();
     $return = array('rows' => array(), 'status' => Horde_ActiveSync_Request_Search::STORE_STATUS_SUCCESS, 'total' => 0);
     try {
         $results = $this->_connector->contacts_search($query['query'], array('pictures' => !empty($query[Horde_ActiveSync_Request_Search::SEARCH_PICTURE])));
     } catch (Horde_ActiveSync_Exception $e) {
         $this->_logger->err($e);
         $this->_endBuffer();
         return $return;
     }
     // Honor range, and don't bother if no results
     $results = array_pop($results);
     $count = count($results);
     if (!$count) {
         $this->_endBuffer();
         return $return;
     }
     $return['total'] = $count;
     $this->_logger->info(sprintf("[%s] Horde_Core_ActiveSync_Driver::_searchGal() found %d matches.", $this->_pid, $count));
     if (!empty($query['range'])) {
         preg_match('/(.*)\\-(.*)/', $query['range'], $matches);
         $return_count = $matches[2] - $matches[1];
         $rows = array_slice($results, $matches[1], $return_count + 1, true);
     }
     $picture_count = 0;
     foreach ($rows as $row) {
         // Explicitly disallow returning contact groups since EAS clients
         // only expect a SINGLE email address to be returned. Returning
         // multiple email addresses, or the group syntax will cause most
         // clients to silently throw out all but the first email address in
         // the list, or will completely fail to send the message altogether.
         if (empty($row['__type']) || $row['__type'] != 'Object') {
             continue;
         }
         $entry = array(Horde_ActiveSync::GAL_ALIAS => !empty($row['alias']) ? $row['alias'] : '', Horde_ActiveSync::GAL_DISPLAYNAME => $row['name'], Horde_ActiveSync::GAL_EMAILADDRESS => !empty($row['email']) ? $row['email'] : '', Horde_ActiveSync::GAL_FIRSTNAME => $row['firstname'], Horde_ActiveSync::GAL_LASTNAME => $row['lastname'], Horde_ActiveSync::GAL_COMPANY => !empty($row['company']) ? $row['company'] : '', Horde_ActiveSync::GAL_HOMEPHONE => !empty($row['homePhone']) ? $row['homePhone'] : '', Horde_ActiveSync::GAL_PHONE => !empty($row['workPhone']) ? $row['workPhone'] : '', Horde_ActiveSync::GAL_MOBILEPHONE => !empty($row['cellPhone']) ? $row['cellPhone'] : '', Horde_ActiveSync::GAL_TITLE => !empty($row['title']) ? $row['title'] : '', Horde_ActiveSync::GAL_OFFICE => !empty($row['office']) ? $row['office'] : '');
         if (!empty($query[Horde_ActiveSync_Request_Search::SEARCH_PICTURE])) {
             $picture = Horde_ActiveSync::messageFactory('GalPicture');
             if (empty($row['photo'])) {
                 $picture->status = Horde_ActiveSync_Status::NO_PICTURE;
             } elseif (!empty($query[Horde_ActiveSync_Request_Search::SEARCH_MAXPICTURES]) && $picture_count > $query[Horde_ActiveSync_Request_Search::SEARCH_MAXPICTURES]) {
                 $picture->status = Horde_ActiveSync_Status::PICTURE_LIMIT_REACHED;
             } elseif (!empty($query[Horde_ActiveSync_Request_Search::SEARCH_MAXSIZE]) && strlen($row['photo']) > $query[Horde_ActiveSync_Request_Search::SEARCH_MAXSIZE]) {
                 $picture->status = Horde_ActiveSync_Status::PICTURE_TOO_LARGE;
             } else {
                 $picture->data = base64_encode($row['photo']['load']['data']);
                 $picture->status = Horde_ActiveSync_Status::PICTURE_SUCCESS;
                 ++$picture_count;
             }
             $entry[Horde_ActiveSync::GAL_PICTURE] = $picture;
         }
         $return['rows'][] = $entry;
     }
     $this->_endBuffer();
     return $return;
 }
Пример #23
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;
 }
Пример #24
0
 /**
  * Handle request
  *
  * @return boolean
  */
 protected function _handle()
 {
     $requests = array();
     if (!$this->_decoder->getElementStartTag(self::MEETINGRESPONSE_MEETINGRESPONSE)) {
         throw new Horde_ActiveSync_Exception('Protocol Error');
     }
     while ($this->_decoder->getElementStartTag(self::MEETINGRESPONSE_REQUEST)) {
         $req = array();
         while (($tag = $this->_decoder->getElementStartTag(self::MEETINGRESPONSE_USERRESPONSE) ? self::MEETINGRESPONSE_USERRESPONSE : ($this->_decoder->getElementStartTag(self::MEETINGRESPONSE_FOLDERID) ? self::MEETINGRESPONSE_FOLDERID : ($this->_decoder->getElementStartTag(self::MEETINGRESPONSE_REQUESTID) ? self::MEETINGRESPONSE_REQUESTID : ($this->_decoder->getElementStartTag(Horde_ActiveSync_Request_Search::SEARCH_LONGID) ? Horde_ActiveSync_Request_Search::SEARCH_LONGID : ($this->_decoder->getElementStartTag(self::MEETINGRESPONSE_INSTANCEID) ? self::MEETINGRESPONSE_INSTANCEID : ($this->_decoder->getElementStartTag(self::MEETINGRESPONSE_SENDRESPONSE) ? self::MEETINGRESPONSE_SENDRESPONSE : -1)))))) != -1) {
             switch ($tag) {
                 case self::MEETINGRESPONSE_USERRESPONSE:
                     $req['response'] = $this->_decoder->getElementContent();
                     if (!$this->_decoder->getElementEndTag()) {
                         throw new Horde_ActiveSync_Exception('Protocol Error');
                     }
                     break;
                 case self::MEETINGRESPONSE_FOLDERID:
                     $req['folderid'] = $this->_activeSync->getCollectionsObject()->getBackendIdForFolderUid($this->_decoder->getElementContent());
                     if (!$this->_decoder->getElementEndTag()) {
                         throw new Horde_ActiveSync_Exception('Protocol Error');
                     }
                     break;
                 case self::MEETINGRESPONSE_REQUESTID:
                     $req['requestid'] = $this->_decoder->getElementContent();
                     if (!$this->_decoder->getElementEndTag()) {
                         throw new Horde_ActiveSync_Exception('Protocol Error');
                     }
                     break;
                 case self::MEETINGRESPONSE_INSTANCEID:
                     // Original UTC time of appointment instance to be modified
                     // sent in EAS 16 to indicate which instance we are
                     // responding to.
                     $req['instanceid'] = $this->_decoder->getElementContent();
                     if (!$this->_decoder->getElementEndTag()) {
                         throw new Horde_ActiveSync_Exception('Protocol Error');
                     }
                     break;
                 case Horde_ActiveSync_Request_Search::SEARCH_LONGID:
                     // Used in EAS 16 when responding from a search result.
                     $req['longid'] = $this->_decoder->getElementContent();
                     if (!$this->_decoder->getElementEndTag()) {
                         throw new Horde_ActiveSync_Exception('Protocol Error');
                     }
                     break;
                 case self::MEETINGRESPONSE_SENDRESPONSE:
                     // Used in EAS 16 as either a flag to indicate the server
                     // should send the iTip response email, and/or to contain
                     // the body of such an email.
                     if ($this->_decoder->isEmptyElement($this->_decoder->getLastStartElement())) {
                         $req['sendresponse'] = true;
                     } else {
                         // elementContent is an AirSyncBaseBody object.
                         $this->_decoder->getElementStartTag(Horde_ActiveSync::AIRSYNCBASE_BODY);
                         $body = Horde_ActiveSync::messageFactory('AirSyncBaseBody');
                         $body->decodeStream($this->_decoder);
                         $req['sendresponse'] = $body;
                         $this->_decoder->getElementEndTag();
                         // AirSyncbaseBody
                         if (!$this->_decoder->getElementEndTag()) {
                             throw new Horde_ActiveSync_Exception('Protocol Error');
                         }
                     }
                     break;
             }
         }
         $requests[] = $req;
         // </self::MEETINGRESPONSE_REQUEST>
         if (!$this->_decoder->getElementEndTag()) {
             throw new Horde_ActiveSync_Exception('Protocol Error');
         }
     }
     // </self::MEETINGRESPONSE>
     if (!$this->_decoder->getElementEndTag()) {
         throw new Horde_ActiveSync_Exception('Protocol Error');
     }
     // Start output, simply the error code, plus the ID of the calendar item
     // that was generated by the accept of the meeting response
     $this->_encoder->StartWBXML();
     $this->_encoder->startTag(self::MEETINGRESPONSE_MEETINGRESPONSE);
     foreach ($requests as $req) {
         try {
             $uid = $this->_driver->meetingResponse($req);
             $status = self::STATUS_SUCCESS;
         } catch (Horde_Exception_NotFound $e) {
             $status = self::STATUS_SERVER_ERROR;
         } catch (Horde_ActiveSync_Exception $e) {
             // Outlook seems to sometimes send the response from the
             // calendar folder instead of the mailbox regardless of where
             // the message is replied to from this will obviously fail,
             // so we should try one last time to get the message from the
             // INBOX. If it was moved to some other mail folder, we have to
             // just give up.
             $this->_logger->info(sprintf('[%s] Trying to find meeting request in INBOX.', $this->_procid));
             $req['folderid'] = 'INBOX';
             try {
                 $uid = $this->_driver->meetingResponse($req);
                 $status = self::STATUS_SUCCESS;
                 $this->_logger->info(sprintf('[%s] Successfully found meeting response in INBOX.', $this->_procid));
             } catch (Horde_ActiveSync_Exception $e) {
                 $this->_logger->err(sprintf('[%s] Meeting request unable to be located.', $this->_procid));
                 $status = self::STATUS_INVALID_REQUEST;
             }
         }
         $this->_encoder->startTag(self::MEETINGRESPONSE_RESULT);
         $this->_encoder->startTag(self::MEETINGRESPONSE_REQUESTID);
         $this->_encoder->content($req['requestid']);
         $this->_encoder->endTag();
         $this->_encoder->startTag(self::MEETINGRESPONSE_STATUS);
         $this->_encoder->content($status);
         $this->_encoder->endTag();
         if ($status == self::STATUS_SUCCESS) {
             $this->_encoder->startTag(self::MEETINGRESPONSE_CALENDARID);
             $this->_encoder->content($uid);
             $this->_encoder->endTag();
         }
         $this->_encoder->endTag();
     }
     $this->_encoder->endTag();
     return true;
 }