In the process, rework some state storage methods. For the file storage backend,
store the state files in a user directory to allow a (as yet non-existant) UI for
allowing the user and/or admin to request a remote device wipe. Also seperate out
the device-specific information such as the policykey, user-agent, device string etc...
into it's own state file. Again, this will be more efficient in a history/sql based state
storage driver.
define("SYNC_TRUNCATION_SEVEN", 7);
define("SYNC_TRUNCATION_ALL", 9);
-define("SYNC_PROVISION_STATUS_SUCCESS", 1);
-define("SYNC_PROVISION_STATUS_PROTERROR", 2);
-define("SYNC_PROVISION_STATUS_SERVERERROR", 3);
-define("SYNC_PROVISION_STATUS_DEVEXTMANAGED", 4);
-define("SYNC_PROVISION_STATUS_POLKEYMISM", 5);
-
-define("SYNC_PROVISION_RWSTATUS_NA", 0);
-define("SYNC_PROVISION_RWSTATUS_OK", 1);
-define("SYNC_PROVISION_RWSTATUS_PENDING", 2);
-define("SYNC_PROVISION_RWSTATUS_WIPED", 3);
-
/**
* Main ActiveSync class. Entry point for performing all ActiveSync operations
*
);
/**
- * Used to track what error code to send back to PIM on failure
+ * Provisioning support
*
- * @var integer
+ * @var string (TODO _constant this)
*/
- protected $_statusCode = 0;
-
protected $_provisioning;
/**
* @param $protocolversion
* @return unknown_type
*/
- public function handleRequest($cmd, $devId, $version)
+ public function handleRequest($cmd, $devId)
{
$class = 'Horde_ActiveSync_Request_' . basename($cmd);
+ $version = $this->getProtocolVersion();
if (class_exists($class)) {
+ //@TODO: Remove version - get it in handle() since we pass self to it anyway
$request = new $class($this->_driver,
$this->_decoder,
$this->_encoder,
return $request->handle($this);
}
- // GetHierarchy is used in v1.0 of the AS protocol, in v2, it is replaced
- // by the FolderSync command
-
// @TODO: Leave the following in place until all are refactored...then throw
// an error if the class does not exist.
switch($cmd) {
/**
* Send the MS_Server-ActiveSync header
+ * (This is the version Exchange 2003 implements)
*
* @return void
*/
*/
public function getPolicyKey()
{
- if (isset($this->_policykey)) {
- return $this->_policykey;
- }
-
/* Policy key headers may be sent in either of these forms: */
$this->_policykey = $this->_request->getHeader('X-Ms-Policykey');
if (empty($this->_policykey)) {
$this->_policykey = $this->_request->getHeader('X-MS-PolicyKey');
}
+
if (empty($this->_policykey)) {
$this->_policykey = 0;
}
}
/**
+ * Get the username for this request.
+ *
+ * @return string The current username
+ */
+ public function getUser()
+ {
+ return $this->_authUser;
+ }
+
+ /**
* Any code to run on log off
*
* @return boolean
/**
* Will (eventually) return an appropriate state object based on the class
* being sync'd.
- * @param <type> $collection
+ *
+ * @param array $collection
*/
public function &getStateObject($collection = array())
{
}
/**
- * Checks if the sent policykey matches the latest policykey on the server
- * TODO: Revisit this once we have refactored state storage
- * @param string $policykey
- * @param string $devid
- *
- * @return status flag
- */
- public function CheckPolicy($policyKey, $devId)
- {
- $status = SYNC_PROVISION_STATUS_SUCCESS;
-// $user_policykey = $this->getPolicyKey($this->_authUser, $this->_authPass, $devId);
-// if ($user_policykey != $policyKey) {
-// $status = SYNC_PROVISION_STATUS_POLKEYMISM;
-// }
-//
- return $status;
- }
-
- /**
- * Return a policy key for given user with a given device id.
- * If there is no combination user-deviceid available, a new key
- * should be generated.
- *
- * @param string $user
- * @param string $pass
- * @param string $devid
- *
- * @return unknown
- */
- public function getPolicyKey($user, $pass, $devid)
- {
- return false;
- }
-
- /**
- * Generate a random policy key. Right now it's a 10-digit number.
+ * Generate a random 10 digit policy key
*
* @return unknown
*/
}
/**
- * Set a new policy key for the given device id.
- *
- * @param string $policykey
- * @param string $devid
- * @return unknown
- */
- public function setPolicyKey($policykey, $devid)
- {
- return false;
- }
-
- /**
* Return a device wipe status
*
* @param string $user
*
* @var mixed
*/
- protected $_provisioning = false;
+ protected $_provisioning;
/**
* The ActiveSync Version
*/
protected $_devId;
+ protected $_userAgent;
+ protected $_deviceType;
+
/**
* Used to track what error code to send back to PIM on failure
*
/* Provisioning support */
$this->_provisioning = $provisioning;
+ /* Useragent and device identification */
+ $this->_userAgent = $request->getHeader('User-Agent');
+ $get = $request->getGetParams();
+ $this->_deviceType = $get['DeviceType'];
+
/* Logger */
$this->_logger;
}
+ /**
+ * Ensure the PIM's policy key is current.
+ *
+ * @param <type> $devId
+ * @return <type>
+ */
+ public function checkPolicyKey($sentKey)
+ {
+ /* Don't attempt if we don't care */
+ if ($this->_provisioning !== false) {
+ $state = $this->_driver->getStateObject();
+ $storedKey = $state->getPolicyKey($this->_devId);
+ /* Loose provsioning should allow a blank key */
+ if ($storedKey != $sentKey &&
+ ($this->_provisioning !== 'loose' ||
+ ($this->_provisioning === 'loose' && !empty($this->_policyKey)))) {
+
+ Horde_ActiveSync::provisioningRequired();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
public function setLogger(Horde_Log_Logger $logger) {
$this->_logger = $logger;
}
public function handle(Horde_ActiveSync $activeSync)
{
- /* Be optomistic */
+ /* Be optimistic */
$this->_statusCode = self::STATUS_SUCCESS;
$this->_logger->info('[Horde_ActiveSync::handleFolderSync] Beginning FOLDERSYNC');
+ /* Check policy */
+ if (!$this->checkPolicyKey($activeSync->getPolicyKey())) {
+ return false;
+ }
+
/* Maps serverid -> clientid for items that are received from the PIM */
$map = array();
*/
class Horde_ActiveSync_Request_GetItemEstimate extends Horde_ActiveSync_Request_Base
{
-
/** Status Codes **/
const STATUS_SUCCESS = 1;
const STATUS_INVALIDCOL = 2;
*/
public function handle(Horde_ActiveSync $activeSync)
{
+ $this->_logger->info('[Horde_ActiveSync::handleFolderSync] Beginning GETITEMESTIMATE');
+
+ /* Check policy */
+ if (!$this->checkPolicyKey($activeSync->getPolicyKey())) {
+ return false;
+ }
+
$status = array();
$collections = array();
$this->_encoder->startTag(SYNC_GETITEMESTIMATE_GETITEMESTIMATE);
foreach ($collections as $collection) {
$this->_encoder->startTag(SYNC_GETITEMESTIMATE_RESPONSE);
-
$this->_encoder->startTag(SYNC_GETITEMESTIMATE_STATUS);
$this->_encoder->content($status[$collection['id']]);
$this->_encoder->endTag();
-
$this->_encoder->startTag(SYNC_GETITEMESTIMATE_FOLDER);
-
$this->_encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERTYPE);
$this->_encoder->content($collection['class']);
$this->_encoder->endTag();
-
$this->_encoder->startTag(SYNC_GETITEMESTIMATE_FOLDERID);
$this->_encoder->content($collection['id']);
$this->_encoder->endTag();
-
$this->_encoder->startTag(SYNC_GETITEMESTIMATE_ESTIMATE);
$importer = new Horde_ActiveSync_ContentsCache();
-
$state = $this->_driver->getStateObject($collection);
$state->loadState($collection['synckey']);
-
$exporter = $this->_driver->GetExporter();
$exporter->init($state, $importer, $collection);
$this->_encoder->content($exporter->GetChangeCount());
-
$this->_encoder->endTag();
-
$this->_encoder->endTag();
-
$this->_encoder->endTag();
}
-
$this->_encoder->endTag();
return true;
class Horde_ActiveSync_Request_Provision extends Horde_ActiveSync_Request_Base
{
+ /* Status Constants */
+ const STATUS_SUCCESS = 1;
+ const STATUS_PROTERROR = 2; // Global status
+ const STATUS_NOTDEFINED = 2; // Policy status
+
+ const STATUS_SERVERERROR = 3; // Global
+ const STATUS_POLICYUNKNOWN = 3; // Policy
+
+ const STATUS_DEVEXTMANAGED = 4; // Global
+ const STATUS_POLICYCORRUPT = 4; // Policy
+
+ const STATUS_POLKEYMISM = 5;
+
+ /* Client -> Server Status */
+ const STATUS_CLIENT_SUCCESS = 1;
+ const STATUS_CLIENT_PARTIAL = 2; // Only pin was enabled.
+ const STATUS_CLIENT_FAILED = 3; // No policies applied at all.
+ const STATUS_CLIENT_THIRDPARTY = 4; // Client provisioned by 3rd party?
+
+ const RWSTATUS_NA = 0;
+ const RWSTATUS_OK = 1;
+ const RWSTATUS_PENDING = 2;
+ const RWSTATUS_WIPED = 3;
+
+ /**
+ * Handle the Provision request. This is a 3-phase process. Phase 1 is
+ * actually the enforcement, when the server rejects a request and forces
+ * the client to perform this PROVISION request...so we are handling phase
+ * 2 (download policies) and 3 (acknowledge policies) here.
+ *
+ * @param Horde_ActiveSync $activeSync The activesync object.
+ *
+ * @return boolean
+ * @throws Horde_ActiveSync_Exception
+ */
public function handle(Horde_ActiveSync $activeSync)
{
+ /* Get the policy key if it was sent */
$policykey = $activeSync->getPolicyKey();
- $status = SYNC_PROVISION_STATUS_SUCCESS;
+ $this->_logger->debug('PIM PolicyKey: ' . $policykey);
+ /* Be optimistic */
+ $status = self::STATUS_SUCCESS;
+ $policyStatus = self::STATUS_SUCCESS;
+
+ /* Start by assuming we are in stage 2 */
$phase2 = true;
if (!$this->_decoder->getElementStartTag(SYNC_PROVISION_PROVISION)) {
- return false;
+ return $this->_globalError(self::STATUS_PROTERROR);
}
- //handle android remote wipe.
+ /* Handle android remote wipe */
if ($this->_decoder->getElementStartTag(SYNC_PROVISION_REMOTEWIPE)) {
if (!$this->_decoder->getElementStartTag(SYNC_PROVISION_STATUS)) {
- return false;
+ return $this->_globalError(self::STATUS_PROTERROR);
}
-
+ // TODO: Look at $status here...
$status = $this->_decoder->getElementContent();
-
- if (!$this->_decoder->getElementEndTag()) {
- return false;
- }
-
- if (!$this->_decoder->getElementEndTag()) {
- return false;
+ if (!$this->_decoder->getElementEndTag() ||
+ !$this->_decoder->getElementEndTag()) {
+ return $this->_globalError(self::STATUS_PROTERROR);
}
} else {
- if (!$this->_decoder->getElementStartTag(SYNC_PROVISION_POLICIES)) {
- return false;
- }
-
- if (!$this->_decoder->getElementStartTag(SYNC_PROVISION_POLICY)) {
- return false;
- }
+ if (!$this->_decoder->getElementStartTag(SYNC_PROVISION_POLICIES) ||
+ !$this->_decoder->getElementStartTag(SYNC_PROVISION_POLICY) ||
+ !$this->_decoder->getElementStartTag(SYNC_PROVISION_POLICYTYPE)) {
- if (!$this->_decoder->getElementStartTag(SYNC_PROVISION_POLICYTYPE)) {
- return false;
+ return $this->_globalError(self::STATUS_PROTERROR);
}
$policytype = $this->_decoder->getElementContent();
if ($policytype != 'MS-WAP-Provisioning-XML') {
- $status = SYNC_PROVISION_STATUS_SERVERERROR;
+ $policyStatus = self::STATUS_POLICYUNKNOWN;
}
if (!$this->_decoder->getElementEndTag()) {//policytype
- return false;
+ return $this->_globalError(self::STATUS_PROTERROR);
}
+ /* POLICYKEY is only sent by client in phase 3 */
if ($this->_decoder->getElementStartTag(SYNC_PROVISION_POLICYKEY)) {
- // This should be Phase 3 of the Provision conversation...
- // We get the intermediate policy key sent back from the client.
- // TODO: Still need to verify the key once we have some kind of
- // storage for it.
$policykey = $this->_decoder->getElementContent();
- if (!$this->_decoder->getElementEndTag()) {
- return false;
- }
+ if (!$this->_decoder->getElementEndTag() ||
+ !$this->_decoder->getElementStartTag(SYNC_PROVISION_STATUS)) {
- if (!$this->_decoder->getElementStartTag(SYNC_PROVISION_STATUS)) {
- return false;
+ return $this->_globalError(self::STATUS_PROTERROR);
+ }
+ if ($this->_decoder->getElementContent() != self::STATUS_SUCCESS) {
+ $policyStatus = self::STATUS_POLICYCORRUPT;
}
-
- $status = $this->_decoder->getElementContent();
- //do status handling
- $status = SYNC_PROVISION_STATUS_SUCCESS;
if (!$this->_decoder->getElementEndTag()) {
- return false;
+ return $this->_globalError(self::STATUS_PROTERROR);
}
$phase2 = false;
}
- if (!$this->_decoder->getElementEndTag()) {//policy
- return false;
- }
+ if (!$this->_decoder->getElementEndTag() ||
+ !$this->_decoder->getElementEndTag()) {
- if (!$this->_decoder->getElementEndTag()) {//policies
- return false;
+ return $this->_globalError(self::STATUS_PROTERROR);
}
+ /* Handle remote wipe for other devices */
if ($this->_decoder->getElementStartTag(SYNC_PROVISION_REMOTEWIPE)) {
if (!$this->_decoder->getElementStartTag(SYNC_PROVISION_STATUS)) {
- return false;
+ return $this->_globalError(self::STATUS_PROTERROR);
}
-
+ // @TODO: look at status here??
$status = $this->_decoder->getElementContent();
- if (!$this->_decoder->getElementEndTag()) {
- return false;
- }
+ if (!$this->_decoder->getElementEndTag() ||
+ !$this->_decoder->getElementEndTag()) {
- if (!$this->_decoder->getElementEndTag()) {
- return false;
+ return $this->_globalError(self::STATUS_PROTERROR);
}
}
}
if (!$this->_decoder->getElementEndTag()) {//provision
- return false;
+ return $this->_globalError(self::STATUS_PROTERROR);
}
+
+ /* Start handling request and sending output */
$this->_encoder->StartWBXML();
// End of Phase 3 - We create the "final" policy key, store it, then
// send it to the client.
+ $state = $this->_driver->getStateObject();
if (!$phase2) {
+ /* Verify intermediate key */
+ if ($state->getPolicyKey($this->_devId) != $policykey) {
+ $policyStatus = self::STATUS_POLKEYMISM;
+ } else {
+ $policykey = $this->_driver->generatePolicyKey();
+ $info = array('policykey' => $policykey,
+ 'useragent' => $this->_userAgent,
+ 'devicetype' => $this->_deviceType);
+ $state->setDeviceInfo($this->_devId, $info);
+ }
+ } elseif (empty($policykey)) {
+ // This is phase2 - we need to set the intermediate key
$policykey = $this->_driver->generatePolicyKey();
- $this->_driver->setPolicyKey($policykey, $this->_devId);
+ $state->setPolicyKey($this->_devId, $policykey);
}
$this->_encoder->startTag(SYNC_PROVISION_PROVISION);
-
$this->_encoder->startTag(SYNC_PROVISION_STATUS);
$this->_encoder->content($status);
$this->_encoder->endTag();
$this->_encoder->startTag(SYNC_PROVISION_POLICIES);
$this->_encoder->startTag(SYNC_PROVISION_POLICY);
-
$this->_encoder->startTag(SYNC_PROVISION_POLICYTYPE);
$this->_encoder->content($policytype);
$this->_encoder->endTag();
$this->_encoder->startTag(SYNC_PROVISION_STATUS);
- $this->_encoder->content($status);
+ $this->_encoder->content($policyStatus);
$this->_encoder->endTag();
$this->_encoder->startTag(SYNC_PROVISION_POLICYKEY);
$this->_encoder->content($policykey);
$this->_encoder->endTag();
- if ($phase2) {
- // If we are in Phase 2, send the security policies.
- // TODO: Configure this!
+
+ /* Send security policies - configure this/move to it's own method...*/
+ if ($phase2 && $status == self::STATUS_SUCCESS && $policyStatus == self::STATUS_SUCCESS) {
$this->_encoder->startTag(SYNC_PROVISION_DATA);
if ($policytype == 'MS-WAP-Provisioning-XML') {
// Set 4131 to 0 to require a PIN, 4133
$this->_encoder->content('<wap-provisioningdoc><characteristic type="SecurityPolicy"><parm name="4131" value="1"/><parm name="4133" value="0"/></characteristic></wap-provisioningdoc>');
- } else {
- $this->_logger->err('Wrong policy type');
- return false;
}
$this->_encoder->endTag();//data
}
$rwstatus = $this->_driver->getDeviceRWStatus($this->_devId);
//wipe data if status is pending or wiped
- if ($rwstatus == SYNC_PROVISION_RWSTATUS_PENDING || $rwstatus == SYNC_PROVISION_RWSTATUS_WIPED) {
+ if ($rwstatus == self::RWSTATUS_PENDING || $rwstatus == self::RWSTATUS_WIPED) {
$this->_encoder->startTag(SYNC_PROVISION_REMOTEWIPE, false, true);
- $this->_driver->setDeviceRWStatus($this->_devId, SYNC_PROVISION_RWSTATUS_WIPED);
+ $this->_driver->setDeviceRWStatus($this->_devId, self::RWSTATUS_WIPED);
//$rwstatus = SYNC_PROVISION_RWSTATUS_WIPED;
}
return true;
}
+ private function _globalError($status)
+ {
+ $this->_encoder->StartWBXML();
+ $this->_encoder->startTag(SYNC_PROVISION_PROVISION);
+ $this->_encoder->startTag(SYNC_PROVISION_STATUS);
+ $this->_encoder->content($status);
+ $this->_encoder->endTag();
+ $this->_encoder->endTag();
+
+ return false;
+ }
+
}
\ No newline at end of file
{
$this->_logger->info('[Horde_ActiveSync::handleSync] Handling SYNC command.');
+ /* Check policy */
+ if (!$this->checkPolicyKey($activeSync->getPolicyKey())) {
+ return false;
+ }
+
/* Be optimistic */
$this->_statusCode = self::STATUS_SUCCESS;
* Loads the initial state from storage for the specified syncKey and
* intializes the stateMachine for use.
*
- * @param string $key The key for the syncState or pingState to load.
+ * @param string $key The key for the syncState or pingState to load.
*
* @return array The state array
*/
abstract public function loadState($syncKey);
/**
+ * Load/initialize the ping state for the specified device.
+ *
+ * @param string $devId
+ */
+ abstract public function initPingState($devId);
+
+ /**
* Load the ping state for the given device id
*
* @param string $devid The device id.
abstract public function isConflict($stat, $type);
/**
+ * Get the specified device's policy key.
+ *
+ * @param string $devId The device id to get key for.
+ *
+ * @return integer The policy key
+ */
+ abstract public function getPolicyKey($devId);
+
+ /**
+ * Set the policy key for the specified device id
+ *
+ * @param string $devId The device id
+ * @param integer $key The policy key
+ *
+ * @return void
+ */
+ abstract public function setPolicyKey($devId, $key);
+
+ /**
* Set the backend driver
* (should really only be called by a backend object when passing this
* object to client code)
/**
* Load the sync state
*
+ * @param string $syncKey The synckey
+ *
* @return void
* @throws Horde_ActiveSync_Exception
*/
public function loadState($syncKey)
{
+ /* Make sure this user's state directory exists */
+ $dir = $this->_stateDir . '/' . $this->_backend->getUser();
+ if (!file_exists($dir)) {
+ if (!mkdir($dir)) {
+ throw new Horde_ActiveSync_Exception('Failed to create user state storage');
+ }
+ }
+
/* Prime the state cache for the first sync */
if (empty($syncKey)) {
$this->_stateCache = array();
return;
}
- // Check if synckey is allowed
+ /* Check if synckey is allowed */
if (!preg_match('/^s{0,1}\{([0-9A-Za-z-]+)\}([0-9]+)$/', $syncKey, $matches)) {
throw new Horde_ActiveSync_Exception('Invalid sync key');
}
- // Cleanup all older syncstates
+ /* Cleanup all older syncstates */
$this->_gc($syncKey);
- // Read current sync state
- $filename = $this->_stateDir . '/' . $syncKey;
+ /* Read current sync state */
+ $filename = $dir . '/' . $syncKey;
if (!file_exists($filename)) {
throw new Horde_ActiveSync_Exception('Sync state not found');
}
*/
public function save()
{
- return file_put_contents($this->_stateDir . '/' . $this->_syncKey, !empty($this->_stateCache) ? serialize($this->_stateCache) : '');
+ return file_put_contents(
+ $this->_stateDir . '/' . $this->_backend->getUser() . '/' . $this->_syncKey,
+ !empty($this->_stateCache) ? serialize($this->_stateCache) : '');
}
/**
}
/**
- * Save folder data for a specific device
+ * Save folder data for a specific device. Used only for compatibility with
+ * older (version 1) ActiveSync requests.
*
- * @param string $devId The device Id
- * @param array $folders The folder data
+ * @param string $devId The device Id
+ * @param array $folders The folder data
*
* @return boolean
* @throws Horde_ActiveSync_Exception
$unique_folders[SYNC_FOLDER_TYPE_CONTACT] = SYNC_FOLDER_TYPE_DUMMY;
}
- if (!file_put_contents($this->_stateDir . '/compat-' . $devId, serialize($unique_folders))) {
+ if (!file_put_contents($this->_stateDir . '/' . $this->_backend->getUser() . '/compat-' . $devId, serialize($unique_folders))) {
$this->logError('_saveFolderData: Data could not be saved!');
throw new Horde_ActiveSync_Exception('Folder data could not be saved');
}
}
/**
- * Get the folder data for a specific device
+ * Get the folder data for a specific collection for a specific device. Used
+ * only with older (version 1) ActiveSync requests.
*
* @param string $devId The device id
* @param string $class The folder class to fetch (Calendar, Contacts etc.)
*/
public function getFolderData($devId, $class)
{
- $filename = $this->_stateDir . '/compat-' . $devId;
+ $filename = $this->_stateDir . '/' . $this->_backend->getUser() . '/compat-' . $devId;
if (file_exists($filename)) {
$arr = unserialize(file_get_contents($filename));
if ($class == "Calendar") {
}
/**
- * Return an array of known folders.
+ * Return an array of known folders. This is essentially the state for a
+ * FOLDERSYNC request. AS uses a seperate synckey for FOLDERSYNC requests
+ * also, so need to treat it as any other collection.
*
* @return array
*/
* Perform any initialization needed to deal with pingStates
* For this driver, it loads the device's state file.
*
- * @param string $devId The device id of the PIM to load PING state for
+ * @param string $devId The device id of the PIM to load PING state for
*
- * @return The $collection array
+ * @return array The $collection array
*/
public function initPingState($devId)
{
$this->_devId = $devId;
- $file = $this->_stateDir . '/' . $devId;
+ $file = $this->_stateDir . '/' . $this->_backend->getUser() . '/' . $devId;
if (file_exists($file)) {
$this->_pingState = unserialize(file_get_contents($file));
} else {
}
/**
- * Load a specific collection's ping state
+ * Obtain the device info array.
+ *
+ * @param <type> $devId
+ */
+ public function getDeviceInfo($devId)
+ {
+ $this->_devId = $devId;
+ $file = $this->_stateDir . '/' . $this->_backend->getUser() . '/info-' . $devId;
+ if (file_exists($file)) {
+ return unserialize(file_get_contents($file));
+ }
+
+ return array('policykey' => 0,
+ 'rwstatus' => 0,
+ 'devicetype' => '',
+ 'useragent' => '');
+ }
+
+ /**
+ * Set new device info
+ *
+ */
+ public function setDeviceInfo($devId, $data)
+ {
+ $this->_devId = $devId;
+ $file = $this->_stateDir . '/' . $this->_backend->getUser() . '/info-' . $devId;
+ return file_put_contents($file, serialize($data));
+ }
+
+ /**
+ * Load a specific collection's ping state. Ping state must already have
+ * been loaded.
*
* @param array $pingCollection The collection array from the PIM request
*
if (!$haveState) {
$this->_logger->debug('Empty state for '. $pingCollection['class']);
- /* Start with empty state cache */
- //$this->_stateCache[$pingCollection['id']] = array();
-
/* Init members for the getChanges call */
$this->_syncKey = $this->_devId;
$this->_collection = $pingCollection;
if (empty($this->_pingState)) {
throw new Horde_ActiveSync_Exception('PING state not initialized');
}
- $state = serialize(array('lifetime' => $this->_pingState['lifetime'], 'collections' => $this->_pingState['collections']));
+ $state = serialize(array('lifetime' => $this->_pingState['lifetime'],
+ 'collections' => $this->_pingState['collections']));
- return file_put_contents($this->_stateDir . '/' . $this->_devId, $state);
+ return file_put_contents($this->_stateDir . '/' . $this->_backend->getUser() . '/' . $this->_devId, $state);
}
/**
}
/**
+ * Obtain the current policy key, if it exists.
*
- * @param <type> $collectionClass The collection we are syncing
+ * @param string $devId The device id to obtain policy key for.
*
- * @return <type>
+ * @return integer The current policy key for this device, or 0 if none
+ * exists.
+ */
+ public function getPolicyKey($devId)
+ {
+ $info = $this->getDeviceInfo($devId);
+ return $info['policykey'];
+ }
+
+ /**
+ * Save a new device policy key to storage.
+ *
+ * @param string $devId The device id
+ * @param integer $key The new policy key
+ */
+ public function setPolicyKey($devId, $key)
+ {
+ $info = $this->getDeviceInfo($devId);
+ $info['policykey'] = $key;
+ $this->setDeviceInfo($devId, $info);
+ }
+
+ /**
+ * Get list of server changes
+ *
+ * @param integer $flags
+ *
+ * @return array
*/
public function getChanges($flags = 0)
{
return $this->_changes;
}
+ /**
+ * Get the number of server changes.
+ *
+ * @return integer
+ */
public function getChangeCount()
{
if (!isset($this->_changes)) {
$this->getChanges();
- //throw new Horde_ActiveSync_Exception('Changes not yet retrieved. Must call getChanges() first');
}
+
return count($this->_changes);
}
*
* @params string $syncKey The sync key
*
+ * @return boolean
* @throws Horde_ActiveSync_Exception
- * @return boolean?
*/
protected function _gc($syncKey)
{
$guid = $matches[1];
$n = $matches[2];
- $dir = opendir($this->_stateDir);
+ $dir = @opendir($this->_stateDir . '/' . $this->_backend->getUser());
if (!$dir) {
return false;
}
while ($entry = readdir($dir)) {
if (preg_match('/^s{0,1}\{([0-9A-Za-z-]+)\}([0-9]+)$/', $entry, $matches)) {
if ($matches[1] == $guid && $matches[2] < $n) {
- unlink($this->_stateDir . '/' . $entry);
+ unlink($this->_stateDir . '/' . $this->_backend->getUser() . '/' . $entry);
}
}
}
}
/**
+ * Helper function that performs the actual diff between PIM state and
+ * server state arrays.
+ *
+ * @param array $old The PIM state
+ * @param array $new The current server state
*
- * @param $old
- * @param $new
* @return unknown_type
*/
private function _getDiff($old, $new)
}
/**
+ * Helper function for the _diff method
*
* @param $a
* @param $b
* @return void
* @throws Horde_ActiveSync_Exception
*/
- public function loadState($syncKey)
+ public function loadState($syncKey, $username)
{
if (empty($syncKey)) {
return;
private $_policykey;
/**
- * ActiveSync protocol version
- * (Sent as either 'Ms-Asprotocolversion' or 'MS-ASProtocolVersion')
- * Should default to '1.0' if not sent.
- *
- * @var string
- */
- private $_protocolVersion;
-
- /**
* Require provisioning? Valid values:
* true - provisioning required
* false - not checked
*/
private $_provisioning;
+
/**
* Constructor.
* Parameters in addition to Horde_Rpc's:
$this->_policykey = $this->_server->getPolicyKey();
}
$this->_server->setProvisioning = $this->_provisioning;
-
- /* Protocol Version */
- $this->_protocolVersion = $this->_server->getProtocolVersion();
}
/**
// Do the actual request
$this->_logger->debug('Horde_Rpc_ActiveSync::getResponse() starting for ' . $this->_get['Cmd']);
- if (!$this->_server->handleRequest($this->_get['Cmd'], $this->_get['DeviceId'], $this->_protocolVersion)) {
- /* @TODO If request failed, try to output a reasonable error to the
- * device if we can...and this should be done from the ActiveSync
- * objects anyway...
- */
- if(!headers_sent()) {
- header('Content-type: text/html');
- echo("<BODY>\n");
- echo("<h3>Error</h3><p>\n");
- echo("There was a problem processing the <i>{$this->_get['Cmd']}</i> command from your PDA.\n");
- echo("</BODY>\n");
- }
- }
+ $this->_server->handleRequest($this->_get['Cmd'], $this->_get['DeviceId']);
+
break;
case 'GET':
echo 'Access denied or user ' . $this->_get['User'] . ' unknown.';
}
- /* Policies / Provisioning */
- if ($this->_provisioning !== false && $this->_request->getServer('REQUEST_METHOD') != 'OPTIONS' &&
- $this->_get['Cmd'] != 'Ping' && $this->_get['Cmd'] != 'Provision' &&
- $this->_backend->CheckPolicy($this->_policykey, $this->_get['DeviceId']) != SYNC_PROVISION_STATUS_SUCCESS &&
- ($this->_provisioning !== 'loose' ||
- ($this->_provisioning === 'loose' && !empty($this->_policyKey)))) {
-
- Horde_ActiveSync::provisioningRequired();
-
- return false;
- }
-
$this->_logger->debug('Horde_Rpc_ActiveSync::authorize() exiting');
return true;