/** * Import a folder change from the wbxml stream * * @param string $uid The folder uid * @param string $displayname The folder display name * @param string $parent The parent folder id. * @param integer $type The EAS Folder type. @since 2.9.0 * * @return Horde_ActiveSync_Message_Folder The new folder object. */ public function importFolderChange($uid, $displayname, $parent = Horde_ActiveSync::FOLDER_ROOT, $type = null) { $this->_logger->info(sprintf('[%s] Horde_ActiveSync_Connector_Importer::importFolderChange(%s, %s, %s, %s)', $this->_procid, $uid, $displayname, $parent, $type)); // Convert the uids to serverids. $collections = $this->_as->getCollectionsObject(); $parent_sid = !empty($parent) ? $collections->getBackendIdForFolderUid($parent) : $parent; $folderid = !empty($uid) ? $collections->getBackendIdForFolderUid($uid) : false; // Perform the creation in the backend. try { $results = $this->_as->driver->changeFolder($folderid, $displayname, $parent_sid, $uid, $type); } catch (Horde_ActiveSync_Exception $e) { $this->_logger->err($e->getMessage()); throw $e; } // @todo Horde 6 this should always return an object. if ($results instanceof Horde_ActiveSync_Message_Folder) { $folderid = $results->_serverid; $uid = $results->serverid; } else { // @TODO Remove for 3.0 Need to build a message folder object here // for BC reasons. $serverid = $results; $results = $this->_as->messageFactory('Folder'); $results->serverid = $serverid; $results->_serverid = $folderid; } $change = array(); $change['id'] = $uid; $change['folderid'] = $folderid; $change['mod'] = $displayname; $change['parent'] = $parent; $this->_state->updateState(Horde_ActiveSync::CHANGE_TYPE_CHANGE, $change, Horde_ActiveSync::CHANGE_ORIGIN_PIM); return $results; }
protected function _logError($error, $name, $runtime = null) { if ($this->_logger) { $name = (empty($name) ? '' : $name) . (empty($runtime) ? '' : sprintf(" (%.4fs)", $runtime)); $this->_logger->err($this->_formatLogEntry($name, $error)); } }
/** * Import an event response into a user's calendar. Used for updating * attendee information from a meeting response. * * @param Horde_Icalendar_vEvent $vEvent The event data. * @param string $attendee The attendee. */ public function calendar_import_attendee(Horde_Icalendar_vEvent $vEvent, $attendee) { if ($this->_registry->hasMethod('calendar/updateAttendee')) { // If the mail interface (i.e., IMP) provides a mime driver for // iTips, check if we are allowed to autoupdate. If we have no // configuration, err on the side of caution and DO NOT auto import. $config = $GLOBALS['injector']->getInstance('Horde_Core_Factory_MimeViewer')->getViewerConfig('text/calendar', $this->_registry->hasInterface('mail')); if ($config[1]['driver'] == 'Itip' && !empty($config[1]['auto_update_eventreply'])) { if (is_array($config[1]['auto_update_eventreply'])) { $adr = new Horde_Mail_Rfc822_Address($attendee); $have_match = false; foreach ($config[1]['auto_update_eventreply'] as $val) { if ($adr->matchDomain($val)) { $have_match = true; break; } } if (!$have_match) { return; } } try { $this->_registry->calendar->updateAttendee($vEvent, $attendee); } catch (Horde_Exception $e) { $this->_logger->err($e->getMessage()); } } } }
/** * * @param Horde_Imap_Client_Mailbox $mbox The mailbox * @param array $uids An array of message uids * @param array $options An options array * - headers: (boolean) Fetch header text if true. * DEFAULT: false (Do not fetch header text). * - structure: (boolean) Fetch message structure. * DEFAULT: true (Fetch message structure). * - flags: (boolean) Fetch messagge flags. * DEFAULT: true (Fetch message flags). * - envelope: (boolen) Fetch the envelope data. * DEFAULT: false (Do not fetch envelope). @since 2.4.0 * * @return Horde_Imap_Fetch_Results The results. * @throws Horde_ActiveSync_Exception */ protected function _getMailMessages(Horde_Imap_Client_Mailbox $mbox, array $uids, array $options = array()) { $options = array_merge(array('headers' => false, 'structure' => true, 'flags' => true, 'envelope' => false), $options); $imap = $this->_getImapOb(); $query = new Horde_Imap_Client_Fetch_Query(); if ($options['structure']) { $query->structure(); } if ($options['flags']) { $query->flags(); } if ($options['envelope']) { $query->envelope(); } if (!empty($options['headers'])) { $query->headerText(array('peek' => true)); } $ids = new Horde_Imap_Client_Ids($uids); try { return $imap->fetch($mbox, $query, array('ids' => $ids, 'exists' => true)); } catch (Horde_Imap_Client_Exception $e) { $this->_logger->err(sprintf('[%s] Unable to fetch message: %s', $this->_procid, $e->getMessage())); throw new Horde_ActiveSync_Exception($e); } }
public function mail_logRecipient($action, $recipients, $message_id) { if ($this->_registry->hasMethod('logRecipient', $this->_registry->hasInterface('mail'))) { try { $this->_registry->mail->logRecipient($action, $recipients, $message_id); } catch (Horde_Exception $e) { $this->_logger->err($e->getMessage()); } } }
/** * React on detection of more than one default folder. * * @param string $first The first default folder name. * @param string $second The second default folder name. * @param string $type The folder type. * @param string $owner The folder owner. */ protected function doubleDefault($first, $second, $owner, $type) { $this->_logger->err(sprintf('Both folders "%s" and "%s" of owner "%s" are marked as default folder of type "%s"!', $first, $second, $owner, $type)); }
/** * Handle device checks. Takes into account permissions and restrictions * via various callback methods. * * @param string $devId The client provided device id. * * @return boolean If EAS version is > 12.1 returns false on any type of * failure in allowing the device to connect. Sets * appropriate internal variables to indicate the type of * error to return to the client. Failure on EAS version * < 12.1 results in throwing exceptions. Otherwise, return * true. * @throws Horde_ActiveSync_Exception, Horde_Exception_AuthenticationFailure */ protected function _handleDevice($devId) { $get = $this->getGetVars(); $version = $this->getProtocolVersion(); // Does device exist AND does the user have an account on the device? if (!$this->_state->deviceExists($devId, $this->_driver->getUser())) { // Device might exist, but with a new (additional) user account if ($this->_state->deviceExists($devId)) { self::$_device = $this->_state->loadDeviceInfo($devId); } else { self::$_device = new Horde_ActiveSync_Device($this->_state); } self::$_device->policykey = 0; self::$_device->userAgent = $this->_request->getHeader('User-Agent'); self::$_device->deviceType = !empty($get['DeviceType']) ? $get['DeviceType'] : ''; self::$_device->rwstatus = self::RWSTATUS_NA; self::$_device->user = $this->_driver->getUser(); self::$_device->id = $devId; self::$_device->needsVersionUpdate($this->getSupportedVersions()); self::$_device->version = $version; // @TODO: Remove is_callable check for H6. // Combine this with the modifyDevice callback? Allow $device // to be modified here? if (is_callable(array($this->_driver, 'createDeviceCallback'))) { $callback_ret = $this->_driver->createDeviceCallback(self::$_device); if ($callback_ret !== true) { $msg = sprintf('The device %s was disallowed for user %s per policy settings.', self::$_device->id, self::$_device->user); self::$_logger->err($msg); // Always throw exception in place of status code since we // won't have a version number before the device is created. throw new Horde_Exception_AuthenticationFailure($msg, $callback_ret); } else { // Give the driver a chance to modify device properties. if (is_callable(array($this->_driver, 'modifyDeviceCallback'))) { self::$_device = $this->_driver->modifyDeviceCallback(self::$_device); } } } } else { self::$_device = $this->_state->loadDeviceInfo($devId, $this->_driver->getUser()); // If the device state was removed from storage, we may lose the // device properties, so try to repopulate what we can. userAgent // is ALWAYS available, so if it's missing, the state is gone. if (empty(self::$_device->userAgent)) { self::$_device->userAgent = $this->_request->getHeader('User-Agent'); self::$_device->deviceType = !empty($get['DeviceType']) ? $get['DeviceType'] : ''; self::$_device->user = $this->_driver->getUser(); } if (empty(self::$_device->version)) { self::$_device->version = $version; } if (self::$_device->version < $this->_maxVersion && self::$_device->needsVersionUpdate($this->getSupportedVersions())) { $this->_needMsRp = true; } // Give the driver a chance to modify device properties. if (is_callable(array($this->_driver, 'modifyDeviceCallback'))) { self::$_device = $this->_driver->modifyDeviceCallback(self::$_device); } } // Save the device now that we know it is at least allowed to connect, // or it has connected successfully at least once in the past. self::$_device->save(); if (is_callable(array($this->_driver, 'deviceCallback'))) { $callback_ret = $this->_driver->deviceCallback(self::$_device); if ($callback_ret !== true) { $msg = sprintf('The device %s was disallowed for user %s per policy settings.', self::$_device->id, self::$_device->user); self::$_logger->err($msg); if ($version > self::VERSION_TWELVEONE) { // Use a status code here, since the device has already // connected. $this->_globalError = $callback_ret; return false; } else { throw new Horde_Exception_AuthenticationFailure($msg, $callback_ret); } } } // Lastly, check if the device has been set to blocked. if (self::$_device->blocked) { $msg = sprintf('The device %s was blocked.', self::$_device->id); self::$_logger->err($msg); if ($version > self::VERSION_TWELVEONE) { $this->_globalError = Horde_ActiveSync_Status::DEVICE_BLOCKED_FOR_USER; return false; } else { throw new Horde_ActiveSync_Exception($msg); } } return true; }
/** * Poll the backend for changes. * * @param integer $heartbeat The heartbeat lifetime to wait for changes. * @param integer $interval The wait interval between poll iterations. * @param array $options An options array containing any of: * - pingable: (boolean) Only poll collections with the pingable flag set. * DEFAULT: false * * @return boolean|integer True if changes were detected in any of the * collections, false if no changes detected * or a status code if failed. */ public function pollForChanges($heartbeat, $interval, array $options = array()) { $dataavailable = false; $started = time(); $until = $started + $heartbeat; $this->_logger->info(sprintf('Waiting for changes for %s seconds', $heartbeat)); // If pinging, make sure we have pingable collections. Note we can't // filter on them here because the collections might change during the // loop below. if (!empty($options['pingable']) && !$this->havePingableCollections()) { $this->_logger->err('No pingable collections.'); return self::COLLECTION_ERR_SERVER; } // Need to update AND SAVE the timestamp for race conditions to be // detected. $this->lasthbsyncstarted = $started; $this->save(); // We only check for remote wipe request once every 5 iterations to // save on DB load since we must reload the device's state each time. $rw_check_countdown = 5; while (($now = time()) < $until) { // Try not to go over the heartbeat interval. if ($until - $now < $interval) { $interval = $until - $now; } // See if another process has altered the sync_cache. if ($this->checkStaleRequest()) { return self::COLLECTION_ERR_STALE; } // Make sure the collections are still there (there might have been // an error in refreshing them from the cache). Ideally this should // NEVER happen. if (!count($this->_collections)) { $this->_logger->err('NO COLLECTIONS! This should not happen!'); return self::COLLECTION_ERR_SERVER; } // Check for WIPE request once every 5 iterations to balance between // performance and speed of catching a remote wipe request. if ($rw_check_countdown-- == 0) { $rw_check_countdown = 5; if ($this->_as->provisioning != Horde_ActiveSync::PROVISIONING_NONE) { $rwstatus = $this->_as->state->getDeviceRWStatus($this->_as->device->id, true); if ($rwstatus == Horde_ActiveSync::RWSTATUS_PENDING || $rwstatus == Horde_ActiveSync::RWSTATUS_WIPED) { return self::COLLECTION_ERR_FOLDERSYNC_REQUIRED; } } } // Check each collection we are interested in. foreach ($this->_collections as $id => $collection) { // Initialize the collection's state data in the state handler. try { $this->initCollectionState($collection, true); } catch (Horde_ActiveSync_Exception_StateGone $e) { $this->_logger->notice(sprintf('[%s] State not found for %s. Continuing.', $this->_procid, $id)); if (!empty($options['pingable'])) { return self::COLLECTION_ERR_PING_NEED_FULL; } $dataavailable = true; $this->setGetChangesFlag($id); continue; } catch (Horde_ActiveSync_Exception_InvalidRequest $e) { // Thrown when state is unable to be initialized because the // collection has not yet been synched, but was requested to // be pinged. $this->_logger->err(sprintf('[%s] Unable to initialize state for %s. Ignoring during pollForChanges: %s.', $this->_procid, $id, $e->getMessage())); continue; } catch (Horde_ActiveSync_Exception_FolderGone $e) { $this->_logger->warn('Folder gone for collection ' . $collection['id']); return self::COLLECTION_ERR_FOLDERSYNC_REQUIRED; } catch (Horde_ActiveSync_Exception $e) { $this->_logger->err('Error loading state: ' . $e->getMessage()); $this->_as->state->loadState(array(), null, Horde_ActiveSync::REQUEST_TYPE_SYNC, $id); $this->setGetChangesFlag($id); $dataavailable = true; continue; } if (!empty($options['pingable']) && !$this->_cache->collectionIsPingable($id)) { $this->_logger->notice(sprintf('[%s] Skipping %s because it is not PINGable.', $this->_procid, $id)); continue; } try { if ($cnt = $this->getCollectionChangeCount(true)) { $dataavailable = true; $this->setGetChangesFlag($id); if (!empty($options['pingable'])) { $this->_cache->setPingChangeFlag($id); } } else { try { $this->_as->state->updateSyncStamp(); } catch (Horde_ActiveSync_Exception $e) { $this->_logger->err($e->getMessage()); } } } catch (Horde_ActiveSync_Exception_StaleState $e) { $this->_logger->notice(sprintf('[%s] SYNC terminating and force-clearing device state: %s', $this->_procid, $e->getMessage())); $this->_as->state->loadState(array(), null, Horde_ActiveSync::REQUEST_TYPE_SYNC, $id); $this->setGetChangesFlag($id); $dataavailable = true; } catch (Horde_ActiveSync_Exception_FolderGone $e) { $this->_logger->notice(sprintf('[%s] SYNC terminating: %s', $this->_procid, $e->getMessage())); // If we are missing a folder, we should clear the PING // cache also, to be sure it picks up any hierarchy changes // since most clients don't seem smart enough to figure this // out on their own. $this->resetPingCache(); return self::COLLECTION_ERR_FOLDERSYNC_REQUIRED; } catch (Horde_Exception_AuthenticationFailure $e) { // We lost authentication for some reason. $this->_logger->err(sprintf('[%s] Authentication lost during PING!!', $this->_procid)); return self::COLLECTION_ERR_AUTHENTICATION; } catch (Horde_ActiveSync_Exception $e) { $this->_logger->err(sprintf('[%s] Sync object cannot be configured, throttling: %s', $this->_procid, $e->getMessage())); $this->_sleep(30); continue; } } if (!empty($dataavailable)) { $this->_logger->info(sprintf('[%s] Found changes!', $this->_procid)); break; } // Wait a bit... $this->_sleep($interval); // Refresh the collections. $this->updateCollectionsFromCache(); } // Check that no other Sync process already started // If so, we exit here and let the other process do the export. if ($this->checkStaleRequest()) { $this->_logger->info('Changes in cache determined during Sync Wait/Heartbeat, exiting here.'); return self::COLLECTION_ERR_STALE; } $this->_logger->info(sprintf('[%s] Looping Sync complete: DataAvailable: %s, DataImported: %s', $this->_procid, $dataavailable, $this->importedChanges)); return $dataavailable; }
/** * 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; } }
/** * Recursively decodes the WBXML from input stream. This means that if this * message contains complex types (like Appointment.Recuurence for example) * the sub-objects are auto-instantiated and decoded as well. Places the * decoded objects in the local properties array. * * @param Horde_ActiveSync_Wbxml_Decoder The stream decoder * * @throws Horde_ActiveSync_Exception */ public function decodeStream(Horde_ActiveSync_Wbxml_Decoder &$decoder) { while (1) { $entity = $decoder->getElement(); if ($entity[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG) { if (!($entity[Horde_ActiveSync_Wbxml::EN_FLAGS] & Horde_ActiveSync_Wbxml::EN_FLAGS_CONTENT)) { $map = $this->_mapping[$entity[Horde_ActiveSync_Wbxml::EN_TAG]]; if (!isset($map[self::KEY_TYPE])) { $this->{$map}[self::KEY_ATTRIBUTE] = ''; } elseif ($map[self::KEY_TYPE] == self::TYPE_DATE || $map[self::KEY_TYPE] == self::TYPE_DATE_DASHES) { $this->{$map}[self::KEY_ATTRIBUTE] = ''; } continue; } // Found start tag if (!isset($this->_mapping[$entity[Horde_ActiveSync_Wbxml::EN_TAG]])) { $this->_logger->err('Tag ' . $entity[Horde_ActiveSync_Wbxml::EN_TAG] . ' unexpected in type XML type ' . get_class($this)); throw new Horde_ActiveSync_Exception('Unexpected tag'); } else { $map = $this->_mapping[$entity[Horde_ActiveSync_Wbxml::EN_TAG]]; if (isset($map[self::KEY_VALUES])) { // Handle arrays of attribute values while (1) { if (!$decoder->getElementStartTag($map[self::KEY_VALUES])) { break; } if (isset($map[self::KEY_TYPE])) { $class = $map[self::KEY_TYPE]; $decoded = new $class(array('protocolversion' => $this->_version, 'logger' => $this->_logger)); $decoded->decodeStream($decoder); } else { $decoded = $decoder->getElementContent(); } if (!isset($this->{$map}[self::KEY_ATTRIBUTE])) { $this->{$map}[self::KEY_ATTRIBUTE] = array($decoded); } else { $this->{$map[self::KEY_ATTRIBUTE]}[] = $decoded; } if (!$decoder->getElementEndTag()) { throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag'); } } if (!$decoder->getElementEndTag()) { return false; } } else { // Handle a simple attribute value if (isset($map[self::KEY_TYPE])) { // Complex type, decode recursively if ($map[self::KEY_TYPE] == self::TYPE_DATE || $map[self::KEY_TYPE] == self::TYPE_DATE_DASHES) { $decoded = $this->_parseDate($decoder->getElementContent()); if (!$decoder->getElementEndTag()) { throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag'); } } elseif ($map[self::KEY_TYPE] == self::TYPE_HEX) { $decoded = self::hex2bin($decoder->getElementContent()); if (!$decoder->getElementEndTag()) { throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag'); } } else { $class = $map[self::KEY_TYPE]; $subdecoder = new $class(array('protocolversion' => $this->_version, 'logger' => $this->_logger)); if ($subdecoder->decodeStream($decoder) === false) { throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag'); } $decoded = $subdecoder; if (!$decoder->getElementEndTag()) { $this->_logger->err('No end tag for ' . $entity[Horde_ActiveSync_Wbxml::EN_TAG]); throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag'); } } } else { // Simple type, just get content $decoded = $decoder->getElementContent(); if ($decoded === false) { $this->_logger->err('Unable to get content for ' . $entity[Horde_ActiveSync_Wbxml::EN_TAG]); } if (!$decoder->getElementEndTag()) { $this->_logger->err('Unable to get end tag for ' . $entity[Horde_ActiveSync_Wbxml::EN_TAG]); throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag'); } } // $decoded now contains data object (or string) $this->{$map}[self::KEY_ATTRIBUTE] = $decoded; } } } elseif ($entity[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG) { $decoder->_ungetElement($entity); break; } else { $this->_logger->err('Unexpected content in type'); break; } } }
$baseImg->applyEffects(); $baseImg->display(); $time = xdebug_time_index() - $time; $memory = xdebug_peak_memory_usage(); logThis($test, $time, $memory); break; case 'testPolaroidstackBlueBG': $imgs = array(getImageObject(array('filename' => 'img1.jpg')), getImageObject(array('filename' => 'img2.jpg')), getImageObject(array('filename' => 'img3.jpg'))); $baseImg = getImageObject(array('width' => 1, 'height' => 1, 'background' => 'none')); $baseImg->addEffect('PhotoStack', array('images' => $imgs, 'resize_height' => 150, 'padding' => 0, 'background' => 'blue', 'type' => 'polaroid')); $baseImg->applyEffects(); $baseImg->display(); break; } } catch (Exception $e) { $logger->err($e); header('Content-Type: image/png'); readfile('error.png'); exit; } /** * Obtain a Horde_Image object * * @param array $params Any additional parameters * * @return Horde_Image_Base The image object. */ function getImageObject($params = array()) { global $convert, $driver, $identify; $context = array('tmpdir' => Horde::getTempdir());
/** * Encodes this object (and any sub-objects) as wbxml to the output stream. * Output is ordered according to $_mapping * * @param Horde_ActiveSync_Wbxml_Encoder $encoder The wbxml stream encoder * @throws Horde_ActiveSync_Exception */ public function encodeStream(Horde_ActiveSync_Wbxml_Encoder &$encoder) { if (!$this->_preEncodeValidation()) { $this->_logger->err(sprintf('Pre-encoding validation failed for %s item', get_class($this))); throw new Horde_ActiveSync_Exception(sprintf('Pre-encoding validation failded for %s item', get_class($this))); } foreach ($this->_mapping as $tag => $map) { if (isset($this->{$map[self::KEY_ATTRIBUTE]})) { // Variable is available if (is_object($this->{$map[self::KEY_ATTRIBUTE]}) && !$this->{$map[self::KEY_ATTRIBUTE]} instanceof Horde_Date) { // Objects can do their own encoding $encoder->startTag($tag); $this->{$map[self::KEY_ATTRIBUTE]}->encodeStream($encoder); $encoder->endTag(); } elseif (isset($map[self::KEY_VALUES]) && is_array($this->{$map[self::KEY_ATTRIBUTE]})) { // Array of objects. Note that some array values must be // send as an empty tag if they contain no elements. if (count($this->{$map[self::KEY_ATTRIBUTE]})) { if (!isset($map[self::KEY_PROPERTY]) || $map[self::KEY_PROPERTY] != self::PROPERTY_NO_CONTAINER) { $encoder->startTag($tag); } foreach ($this->{$map[self::KEY_ATTRIBUTE]} as $element) { if (is_object($element)) { // Hanlde multi-typed array containers. if (is_array($map[self::KEY_VALUES])) { $idx = array_search(get_class($element), $map[self::KEY_TYPE]); $tag = $map[self::KEY_VALUES][$idx]; } else { $tag = $map[self::KEY_VALUES]; } // Outputs object container (eg Attachment) $encoder->startTag($tag); $element->encodeStream($encoder); $encoder->endTag(); } else { // Do not ever output empty items here if (strlen($element) > 0) { $encoder->startTag($map[self::KEY_VALUES]); $encoder->content($element); $encoder->endTag(); } } } if (!isset($map[self::KEY_PROPERTY]) || $map[self::KEY_PROPERTY] != self::PROPERTY_NO_CONTAINER) { $encoder->endTag(); } } elseif ($this->_checkSendEmpty($tag)) { $encoder->startTag($tag, null, true); } } else { // Simple type if (!is_resource($this->{$map[self::KEY_ATTRIBUTE]}) && strlen($this->{$map[self::KEY_ATTRIBUTE]}) == 0) { // Do not output empty items except for the following: if ($this->_checkSendEmpty($tag)) { $encoder->startTag($tag, $this->{$map[self::KEY_ATTRIBUTE]}, true); } continue; } elseif ($encoder->multipart && in_array($tag, array(Horde_ActiveSync::SYNC_DATA, Horde_ActiveSync::AIRSYNCBASE_DATA, Horde_ActiveSync_Request_ItemOperations::ITEMOPERATIONS_DATA))) { $this->_logger->info('HANDLING MULTIPART OUTPUT'); $encoder->addPart($this->{$map[self::KEY_ATTRIBUTE]}); $encoder->startTag(Horde_ActiveSync_Request_ItemOperations::ITEMOPERATIONS_PART); $encoder->content((string) (count($encoder->getParts()) - 1)); $encoder->endTag(); continue; } $encoder->startTag($tag); if (isset($map[self::KEY_TYPE]) && in_array($map[self::KEY_TYPE], array(self::TYPE_DATE, self::TYPE_DATE_DASHES, self::TYPE_DATE_LOCAL))) { if (!empty($this->{$map[self::KEY_ATTRIBUTE]})) { // don't output 1-1-1970 $encoder->content($this->_formatDate($this->{$map[self::KEY_ATTRIBUTE]}, $map[self::KEY_TYPE])); } } elseif (isset($map[self::KEY_TYPE]) && $map[self::KEY_TYPE] == self::TYPE_HEX) { $encoder->content(Horde_String::upper(bin2hex($this->{$map[self::KEY_ATTRIBUTE]}))); } elseif (isset($map[self::KEY_TYPE]) && $map[self::KEY_TYPE] == self::TYPE_MAPI_STREAM) { $encoder->content($this->{$map[self::KEY_ATTRIBUTE]}); } else { $encoder->content($this->_checkEncoding($this->{$map[self::KEY_ATTRIBUTE]}, $tag)); } $encoder->endTag(); } } } }
/** * Recursively decodes the WBXML from input stream. This means that if this * message contains complex types (like Appointment.Recuurence for example) * the sub-objects are auto-instantiated and decoded as well. Places the * decoded objects in the local properties array. * * @param Horde_ActiveSync_Wbxml_Decoder The stream decoder * * @throws Horde_ActiveSync_Exception */ public function decodeStream(Horde_ActiveSync_Wbxml_Decoder &$decoder) { while (1) { $entity = $decoder->getElement(); if ($entity[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG) { if (!($entity[Horde_ActiveSync_Wbxml::EN_FLAGS] & Horde_ActiveSync_Wbxml::EN_FLAGS_CONTENT)) { $map = $this->_mapping[$entity[Horde_ActiveSync_Wbxml::EN_TAG]]; if (!isset($map[self::KEY_TYPE])) { $this->{$map[self::KEY_ATTRIBUTE]} = ''; } elseif ($map[self::KEY_TYPE] == self::TYPE_DATE || $map[self::KEY_TYPE] == self::TYPE_DATE_DASHES) { $this->{$map[self::KEY_ATTRIBUTE]} = ''; } continue; } // Found start tag if (!isset($this->_mapping[$entity[Horde_ActiveSync_Wbxml::EN_TAG]])) { $this->_logger->err('Tag ' . $entity[Horde_ActiveSync_Wbxml::EN_TAG] . ' unexpected in type XML type ' . get_class($this)); throw new Horde_ActiveSync_Exception('Unexpected tag'); } else { $map = $this->_mapping[$entity[Horde_ActiveSync_Wbxml::EN_TAG]]; if (isset($map[self::KEY_VALUES])) { // Handle arrays of attribute values while (1) { //do not get start tag for an array without a container if (!(isset($map[self::KEY_PROPERTY]) && $map[self::KEY_PROPERTY] == self::PROPERTY_NO_CONTAINER)) { if (!$decoder->getElementStartTag($map[self::KEY_VALUES])) { break; } } if (isset($map[self::KEY_TYPE])) { $class = $map[self::KEY_TYPE]; $decoded = new $class(array('protocolversion' => $this->_version, 'logger' => $this->_logger)); $decoded->decodeStream($decoder); } else { $decoded = $decoder->getElementContent(); } if (!isset($this->{$map[self::KEY_ATTRIBUTE]})) { $this->{$map[self::KEY_ATTRIBUTE]} = array($decoded); } else { $this->{$map[self::KEY_ATTRIBUTE]}[] = $decoded; } if (!$decoder->getElementEndTag()) { throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag'); } if (isset($map[self::KEY_PROPERTY]) && $map[self::KEY_PROPERTY] == self::PROPERTY_NO_CONTAINER) { $e = $decoder->peek(); //go back to the initial while if another block of no container elements is found if ($e[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG) { continue 2; } //break on end tag because no container elements block end is reached if ($e[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG) { break; } if (empty($e)) { break; } } } //do not get container end tag for an array without a container if (!(isset($map[self::KEY_PROPERTY]) && $map[self::KEY_PROPERTY] == self::PROPERTY_NO_CONTAINER)) { if (!$decoder->getElementEndTag()) { return false; } } } else { // Handle a simple attribute value if (isset($map[self::KEY_TYPE])) { // Complex type, decode recursively if ($map[self::KEY_TYPE] == self::TYPE_DATE || $map[self::KEY_TYPE] == self::TYPE_DATE_DASHES) { $decoded = $this->_parseDate($decoder->getElementContent()); } elseif ($map[self::KEY_TYPE] == self::TYPE_HEX) { $decoded = self::hex2bin($decoder->getElementContent()); } else { $class = $map[self::KEY_TYPE]; $subdecoder = new $class(array('protocolversion' => $this->_version, 'logger' => $this->_logger)); $subdecoder->decodeStream($decoder); $decoded = $subdecoder; } } else { // Simple type, just get content $decoded = $decoder->getElementContent(); if ($decoded === false) { $decoded = ''; $this->_logger->notice('Unable to get expected content for ' . $entity[Horde_ActiveSync_Wbxml::EN_TAG] . ': Setting to an empty string.'); } } if (!$decoder->getElementEndTag()) { $this->_logger->err('Unable to get end tag for ' . $entity[Horde_ActiveSync_Wbxml::EN_TAG]); throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag'); } $this->{$map[self::KEY_ATTRIBUTE]} = $decoded; } } } elseif ($entity[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG) { $decoder->_ungetElement($entity); break; } else { $this->_logger->err('Unexpected content in type'); break; } } }
/** * The heart of the server. Dispatch a request to the appropriate request * handler. * * @param string $cmd The command we are requesting. * @param string $devId The device id making the request. @deprecated * * @return string|boolean false if failed, true if succeeded and response * content is wbxml, otherwise the * content-type string to send in the response. * @throws Horde_ActiveSync_Exception * @throws Horde_ActiveSync_Exception_InvalidRequest * @throws Horde_ActiveSync_PermissionDenied */ public function handleRequest($cmd, $devId) { $get = $this->getGetVars(); if (empty($cmd)) { $cmd = $get['Cmd']; } if (empty($devId)) { $devId = !empty($get['DeviceId']) ? Horde_String::upper($get['DeviceId']) : null; } else { $devId = Horde_String::upper($devId); } $this->_setLogger($get); // @TODO: Remove is_callable check for H6. // Callback to give the backend the option to limit EAS version based // on user/device/etc... if (is_callable(array($this->_driver, 'versionCallback'))) { $this->_driver->versionCallback($this); } // Autodiscovery handles authentication on it's own. if ($cmd == 'Autodiscover') { $request = new Horde_ActiveSync_Request_Autodiscover($this, new Horde_ActiveSync_Device($this->_state)); if (!empty(self::$_logger)) { $request->setLogger(self::$_logger); } $result = $request->handle($this->_request); $this->_driver->clearAuthentication(); return $result; } if (!$this->authenticate(new Horde_ActiveSync_Credentials($this))) { $this->activeSyncHeader(); $this->versionHeader(); $this->commandsHeader(); throw new Horde_Exception_AuthenticationFailure(); } self::$_logger->info(sprintf('[%s] %s request received for user %s', $this->_procid, Horde_String::upper($cmd), $this->_driver->getUser())); // These are all handled in the same class. if ($cmd == 'FolderDelete' || $cmd == 'FolderUpdate') { $cmd = 'FolderCreate'; } // Device id is REQUIRED if (empty($devId)) { if ($cmd == 'Options') { $this->_doOptionsRequest(); $this->_driver->clearAuthentication(); return true; } $this->_driver->clearAuthentication(); throw new Horde_ActiveSync_Exception_InvalidRequest('Device failed to send device id.'); } // EAS Version $version = $this->getProtocolVersion(); // Does device exist AND does the user have an account on the device? if (!$this->_state->deviceExists($devId, $this->_driver->getUser())) { // Device might exist, but with a new (additional) user account if ($this->_state->deviceExists($devId)) { self::$_device = $this->_state->loadDeviceInfo($devId); } else { self::$_device = new Horde_ActiveSync_Device($this->_state); } self::$_device->policykey = 0; self::$_device->userAgent = $this->_request->getHeader('User-Agent'); self::$_device->deviceType = !empty($get['DeviceType']) ? $get['DeviceType'] : ''; self::$_device->rwstatus = self::RWSTATUS_NA; self::$_device->user = $this->_driver->getUser(); self::$_device->id = $devId; self::$_device->needsVersionUpdate($this->getSupportedVersions()); self::$_device->version = $version; // @TODO: Remove is_callable check for H6. // Combine this with the modifyDevice callback? Allow $device // to be modified here? if (is_callable(array($this->_driver, 'createDeviceCallback'))) { $callback_ret = $this->_driver->createDeviceCallback(self::$_device); if ($callback_ret !== true) { $msg = sprintf('The device %s was disallowed for user %s per policy settings.', self::$_device->id, self::$_device->user); self::$_logger->err($msg); if ($version > self::VERSION_TWELVEONE) { $this->_globalError = $callback_ret; } else { throw new Horde_ActiveSync_Exception($msg); } } else { // Give the driver a chance to modify device properties. if (is_callable(array($this->_driver, 'modifyDeviceCallback'))) { self::$_device = $this->_driver->modifyDeviceCallback(self::$_device); } } } } else { self::$_device = $this->_state->loadDeviceInfo($devId, $this->_driver->getUser()); // If the device state was removed from storage, we may lose the // device properties, so try to repopulate what we can. userAgent // is ALWAYS available, so if it's missing, the state is gone. if (empty(self::$_device->userAgent)) { self::$_device->userAgent = $this->_request->getHeader('User-Agent'); self::$_device->deviceType = !empty($get['DeviceType']) ? $get['DeviceType'] : ''; self::$_device->user = $this->_driver->getUser(); } if (empty(self::$_device->version)) { self::$_device->version = $version; } if (self::$_device->version < $this->_maxVersion && self::$_device->needsVersionUpdate($this->getSupportedVersions())) { $needMsRp = true; } // Give the driver a chance to modify device properties. if (is_callable(array($this->_driver, 'modifyDeviceCallback'))) { self::$_device = $this->_driver->modifyDeviceCallback(self::$_device); } } self::$_device->save(); if (is_callable(array($this->_driver, 'deviceCallback'))) { $callback_ret = $this->_driver->deviceCallback(self::$_device); if ($callback_ret !== true) { $msg = sprintf('The device %s was disallowed for user %s per policy settings.', self::$_device->id, self::$_device->user); self::$_logger->err($msg); if ($version > self::VERSION_TWELVEONE) { $this->_globalError = $callback_ret; } else { throw new Horde_ActiveSync_Exception($msg); } } } // Lastly, check if the device has been set to blocked. if (self::$_device->blocked) { $msg = sprintf('The device %s was blocked.', self::$_device->id); self::$_logger->err($msg); if ($version > self::VERSION_TWELVEONE) { $this->_globalError = Horde_ActiveSync_Status::DEVICE_BLOCKED_FOR_USER; } else { throw new Horde_ActiveSync_Exception($msg); } } // Don't bother with everything else if all we want are Options if ($cmd == 'Options') { $this->_doOptionsRequest(); $this->_driver->clearAuthentication(); return true; } // Set provisioning support now that we are authenticated. $this->setProvisioning($this->_driver->getProvisioning(self::$_device)); // Read the initial Wbxml header $this->_decoder->readWbxmlHeader(); // Support Multipart response for ITEMOPERATIONS requests? $headers = $this->_request->getHeaders(); if (!empty($headers['ms-asacceptmultipart']) && $headers['ms-asacceptmultipart'] == 'T' || !empty($get['AcceptMultiPart'])) { $this->_multipart = true; self::$_logger->info(sprintf('[%s] Requesting multipart data.', $this->_procid)); } // Load the request handler to handle the request // We must send the eas header here, since some requests may start // output and be large enough to flush the buffer (e.g., GetAttachment) // See Bug: 12486 $this->activeSyncHeader(); if ($cmd != 'GetAttachment') { $this->contentTypeHeader(); } // Should we announce a new version is available to the client? if (!empty($needMsRp)) { self::$_logger->info(sprintf('[%s] Announcing X-MS-RP to client.', $this->_procid)); header("X-MS-RP: " . $this->getSupportedVersions()); } // @TODO: Look at getting rid of having to set the version in the driver // and get it from the device object for H6. $this->_driver->setDevice(self::$_device); $class = 'Horde_ActiveSync_Request_' . basename($cmd); if (class_exists($class)) { $request = new $class($this); $request->setLogger(self::$_logger); $result = $request->handle(); self::$_logger->info(sprintf('[%s] Maximum memory usage for ActiveSync request: %d bytes.', $this->_procid, memory_get_peak_usage(true))); return $result; } $this->_driver->clearAuthentication(); throw new Horde_ActiveSync_Exception_InvalidRequest(basename($cmd) . ' not supported.'); }
/** * Non-static wrapper for getNewSyncKey. * * @param string $syncKey The old syncKey * * @return string The new synckey * @throws Horde_ActiveSync_Exception * * @todo Remove/replace in H6 with Horde_ActiveSync_SyncKey */ public function getNewSyncKeyWrapper($syncKey) { if ($this->checkCollision($newKey = self::getNewSyncKey($syncKey))) { $this->_logger->err(sprintf('[%s] Found collision when generating synckey %s. Trying again.', $this->_procid, $newKey)); return $this->getNewSyncKeyWrapper($synckey); } return $newKey; }