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; }
/** * 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); }
/** * 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; } }
/** * 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); }
/** * @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; }
/** * 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); }
/** * 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; }
/** * 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; }
/** * 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; } } }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; } }
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)); }
/** * 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'; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }