/** * 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(); // Device. Even though versions of EAS > 12.1 are supposed to send // EAS status codes back to indicate various errors in allowing a client // to connect, we just throw an exception (thus causing a HTTP error // code to be sent as in versions 12.1 and below). Until we refactor for // Horde 6, we don't know the response type to wrap the status code in // until we load the request handler, which requires we start to parse // the WBXML stream and device information etc... This saves resources // as well as keeps things cleaner until we refactor. $device_result = $this->_handleDevice($devId); // 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($this->_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.'); }
/** * 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; } } }
public function testRecurrenceDSTSwitch() { // Recurring event starts 10/1/2011 15:00:00 EDST $l = new Horde_Test_Log(); $logger = $l->getLogger(); // Test Decoding $stream = fopen(__DIR__ . '/fixtures/dst.wbxml', 'r+'); $decoder = new Horde_ActiveSync_Wbxml_Decoder($stream); $element = $decoder->getElementStartTag(Horde_ActiveSync::SYNC_DATA); $appt = new Horde_ActiveSync_Message_Appointment(array('logger' => $logger)); $appt->decodeStream($decoder); fclose($stream); $decoder->getElementEndTag(); $rrule = $appt->getRecurrence(); // Get the next recurrence, still during EDST $next = $rrule->nextActiveRecurrence(new Horde_Date('2011-10-15')); $this->assertEquals('2011-10-15 15:00:00', (string) $next->setTimezone('America/New_York')); // Now get an occurence after the transition to EST. $next = $rrule->nextActiveRecurrence(new Horde_Date('2011-12-01')); $this->assertEquals('2011-12-10 15:00:00', (string) $next->setTimezone('America/New_York')); }
/** * 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(sprintf('Tag %s unexpected in type XML type %s.', $entity[Horde_ActiveSync_Wbxml::EN_TAG], 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 we can have multiple types of objects in this // container, or we are parsing a NO_CONTAINER, // check that we are not at the end tag of the // or we have a valid start tag for the NO_CONTAINER // object. If not, break out of loop. if (is_array($map[self::KEY_VALUES])) { $token = $decoder->peek(); if ($token[Horde_ActiveSync_Wbxml_Decoder::EN_TYPE] == Horde_ActiveSync_Wbxml_Decoder::EN_TYPE_ENDTAG) { break; } } elseif (!(isset($map[self::KEY_PROPERTY]) && $map[self::KEY_PROPERTY] == self::PROPERTY_NO_CONTAINER) && !$decoder->getElementStartTag($map[self::KEY_VALUES])) { break; } // We know we have some valid value, parse out what // it is. Either an array of (possibly varied) // objects, a single object, or simple value. if (is_array($map[self::KEY_VALUES])) { $token = $decoder->getToken(); if (($idx = array_search($token[Horde_ActiveSync_Wbxml_Decoder::EN_TAG], $map[self::KEY_VALUES])) !== false) { $class = $map[self::KEY_TYPE][$idx]; $decoded = new $class(array('protocolversion' => $this->_version, 'logger' => $this->_logger)); $decoded->commandType = $this->commandType; $decoded->decodeStream($decoder); } else { throw new Horde_ActiveSync_Exception('Error in message map configuration'); } } elseif (isset($map[self::KEY_TYPE])) { $class = $map[self::KEY_TYPE]; $decoded = new $class(array('protocolversion' => $this->_version, 'logger' => $this->_logger)); $decoded->commandType = $this->commandType; $decoded->decodeStream($decoder); } else { $decoded = $decoder->getElementContent(); } // Assign the parsed value to the mapped attribute. if (!isset($this->{$map[self::KEY_ATTRIBUTE]})) { $this->{$map[self::KEY_ATTRIBUTE]} = array($decoded); } else { $this->{$map[self::KEY_ATTRIBUTE]}[] = $decoded; } // Get the end tag of this attribute node. if (!$decoder->getElementEndTag()) { throw new Horde_ActiveSync_Exception('Missing expected wbxml end tag'); } // For NO_CONTAINER attributes, need some magic to // make sure we break out properly. 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 a non-container element is found. if ($e[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG) { continue 2; } // Break on end tag because no other container // elements block end is reached. if ($e[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG || 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) && !$decoder->getElementEndTag()) { return false; } } else { // Handle a simple attribute value if (isset($map[self::KEY_TYPE])) { if (in_array($map[self::KEY_TYPE], array(self::TYPE_DATE, self::TYPE_DATE_DASHES, self::TYPE_DATE_LOCAL))) { $decoded = $this->_parseDate($decoder->getElementContent()); } elseif ($map[self::KEY_TYPE] == self::TYPE_HEX) { $decoded = self::_hex2bin($decoder->getElementContent()); } else { // Complex type, decode recursively $class = $map[self::KEY_TYPE]; $subdecoder = new $class(array('protocolversion' => $this->_version, 'logger' => $this->_logger)); $subdecoder->commandType = $this->commandType; $subdecoder->decodeStream($decoder); $decoded = $subdecoder; } } else { // Simple type, just get content $decoded = $decoder->getElementContent(); if ($decoded === false) { $decoded = ''; $this->_logger->notice(sprintf('Unable to get expected content for %s: Setting to an empty string.', $entity[Horde_ActiveSync_Wbxml::EN_TAG])); } } if (!$decoder->getElementEndTag()) { $this->_logger->err(sprintf('Unable to get end tag for %s.', $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; } } if (!$this->_validateDecodedValues()) { throw new Horde_ActiveSync_Exception(sprintf('Invalid values detected in %s.', get_class($this))); } }
/** * 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.'); }