Allow each device to have mulitple user accounts
authorMichael J. Rubinsky <mrubinsk@horde.org>
Wed, 12 May 2010 14:56:37 +0000 (10:56 -0400)
committerMichael J. Rubinsky <mrubinsk@horde.org>
Wed, 12 May 2010 15:26:13 +0000 (11:26 -0400)
SQL update script must be run. It's necessary to force the device to refresh - sorry, Jan ;).
You might be able to get away with only refreshing one collection (like calendars), or even just
pretend to edit the account settings and re-save them (which usually produces a foldersync request).

Alternatively, you can manually add the username/device mapping if it's known to the users table,
and add the username to the other new username fields in the map table and state table.

to the database tables if they are known.

20 files changed:
framework/ActiveSync/lib/Horde/ActiveSync.php
framework/ActiveSync/lib/Horde/ActiveSync/Driver/Horde.php
framework/ActiveSync/lib/Horde/ActiveSync/Request/Base.php
framework/ActiveSync/lib/Horde/ActiveSync/Request/Ping.php
framework/ActiveSync/lib/Horde/ActiveSync/State/Base.php
framework/ActiveSync/lib/Horde/ActiveSync/State/File.php
framework/ActiveSync/lib/Horde/ActiveSync/State/History.php
framework/ActiveSync/test/Horde/ActiveSync/HordeDriverTest.php
horde/config/conf.xml
horde/lib/Prefs/Ui.php
horde/scripts/sql/create.mssql.sql
horde/scripts/sql/create.mysql.sql
horde/scripts/sql/create.oci8.sql
horde/scripts/sql/create.pgsql.sql
horde/scripts/sql/create.sql
horde/scripts/sql/create.sybase.sql
horde/scripts/sql/create.xml
horde/scripts/sql/horde_activesync.sql
horde/scripts/upgrades/2010-05-11_horde_activesync_adduserkey.sql [new file with mode: 0644]
horde/templates/prefs/activesync.html

index fa68ee8..a498323 100644 (file)
@@ -850,7 +850,7 @@ class Horde_ActiveSync
             throw new Horde_ActiveSync_Exception('Device failed to send device id.');
         }
         $state = $this->_driver->getStateObject();
-        if (!empty($devId) && !$state->deviceExists($devId)) {
+        if (!empty($devId) && !$state->deviceExists($devId, $this->_driver->getUser())) {
             $get = $this->_request->getGetParams();
             $device = new StdClass();
             $device->userAgent = $this->_request->getHeader('User-Agent');
@@ -860,6 +860,9 @@ class Horde_ActiveSync
             $device->user = $this->_driver->getUser();
             $device->id = $devId;
             $state->setDeviceInfo($device);
+        } elseif (!empty($devId)) {
+            // @TODO: Check if the empty check is necessary
+            $device = $state->getDeviceInfo($devId, $this->_driver->getUser());
         }
 
         /* Load the request handler to handle the request */
@@ -871,7 +874,7 @@ class Horde_ActiveSync
                                   $this->_encoder,
                                   $this->_request,
                                   $this,
-                                  $devId,
+                                  $device,
                                   $this->_provisioning);
             $request->setLogger($this->_logger);
 
index ade5caf..d10ed15 100644 (file)
@@ -478,7 +478,7 @@ class Horde_ActiveSync_Driver_Horde extends Horde_ActiveSync_Driver_Base
      * @param string $id        The server's uid for the message if this is a
      *                          change to an existing message.
      * @param Horde_ActiveSync_Message_Base $message  The activesync message
-     * @param stdClass $device  The device information
+     * @param object $device  The device information
      *
      * @see framework/ActiveSync/lib/Horde/ActiveSync/Driver/Horde_ActiveSync_Driver_Base#changeMessage($folderid, $id, $message)
      */
index bea0b85..ca053ae 100644 (file)
@@ -110,7 +110,7 @@ abstract class Horde_ActiveSync_Request_Base
                                 Horde_ActiveSync_Wbxml_Encoder $encoder,
                                 Horde_Controller_Request_Http $request,
                                 Horde_ActiveSync $as,
-                                $devId,
+                                $device,
                                 $provisioning)
     {
         /* Backend driver */
@@ -133,7 +133,7 @@ abstract class Horde_ActiveSync_Request_Base
         $this->_state = &$driver->getStateObject();
 
         /* Device info */
-        $this->_device = $this->_state->getDeviceInfo($devId);
+        $this->_device = $device;
     }
 
     /**
@@ -149,7 +149,7 @@ abstract class Horde_ActiveSync_Request_Base
          * header - which is against the specification. Check the user agent
          * for Android (maybe need version sniffing in the future) and set the
          * policykey to null for those devices. */
-         $this->_device = $this->_state->getDeviceInfo($this->_device->id);
+         $this->_device = $this->_state->getDeviceInfo($this->_device->id, $this->_driver->getUser());
          if (strpos($this->_device->userAgent, 'Android') !== false) {
              $sentKey = null;
          }
index dc92565..e604dc2 100644 (file)
@@ -84,12 +84,12 @@ class Horde_ActiveSync_Request_Ping extends Horde_ActiveSync_Request_Base
 
         /* Initialize the state machine */
         $this->_state = &$this->_driver->getStateObject();
-        $this->_state->getDeviceInfo($this->_device->id);
+        $this->_state->getDeviceInfo($this->_device->id, $this->_driver->getUser());
 
         /* See if we have an existing PING state. Need to do this here, before
          * we read in the PING request since the PING request is allowed to omit
          * sections if they have been sent previously */
-        $collections = array_values($this->_state->initPingState($this->_device->id));
+        $collections = array_values($this->_state->initPingState($this->_device));
         $lifetime = $this->_checkHeartbeat($this->_state->getHeartbeatInterval());
 
         /* Build the $collections array if we receive request from PIM */
index 7c5a0d3..4e53c91 100644 (file)
@@ -93,7 +93,7 @@ abstract class Horde_ActiveSync_State_Base
     /**
      * Device info cache
      *
-     * @var array
+     * @var object
      */
     protected $_deviceInfo;
 
@@ -123,6 +123,9 @@ abstract class Horde_ActiveSync_State_Base
     public function __construct($params = array())
     {
         $this->_params = $params;
+        if (empty($params['logger'])) {
+            $this->_logger = new Horde_Support_Stub();
+        }
     }
 
     /**
@@ -167,8 +170,13 @@ abstract class Horde_ActiveSync_State_Base
      */
     public function getPolicyKey($devId)
     {
-        $info = $this->getDeviceInfo($devId);
-        return $info->policykey;
+        //@TODO - combine _devId and _deviceInfo
+        /* See if we have it already */
+        if (empty($this->_deviceInfo) || $this->_devId != $devId) {
+            throw new Horde_ActiveSync_Exception('Device not loaded.');
+        }
+
+        return $this->_deviceInfo->policykey;
     }
 
     /**
@@ -180,8 +188,13 @@ abstract class Horde_ActiveSync_State_Base
      */
     public function getDeviceRWStatus($devId)
     {
-        $info = $this->getDeviceInfo($devId);
-        return $info->rwstatus;
+        //@TODO - combine _devId and _deviceInfo
+        /* See if we have it already */
+        if (empty($this->_deviceInfo) || $this->_devId != $devId) {
+            throw new Horde_ActiveSync_Exception('Device not loaded.');
+        }
+
+        return $this->_deviceInfo->rwstatus;
     }
 
     /**
@@ -441,9 +454,9 @@ abstract class Horde_ActiveSync_State_Base
     /**
      * Load/initialize the ping state for the specified device.
      *
-     * @param string $devId
+     * @param object $device
      */
-    abstract public function initPingState($devId);
+    abstract public function initPingState($device);
 
     /**
      * Load the ping state for the given device id
@@ -476,6 +489,29 @@ abstract class Horde_ActiveSync_State_Base
     abstract public function updateState($type, $change, $origin = Horde_ActiveSync::CHANGE_ORIGIN_NA);
 
     /**
+     * Save folder data for a specific device. This is needed for BC with older
+     * activesync versions that use GETHIERARCHY requests to get the folder info
+     * instead of maintaining the folder state with FOLDERSYNC requests.
+     *
+     * @param object $device  The device object
+     * @param array $folders  The folder data
+     *
+     * @return boolean
+     * @throws Horde_ActiveSync_Exception
+     */
+    abstract public function setFolderData($device, $folders);
+
+    /**
+     * Get the folder data for a specific device
+     *
+     * @param object $device  The device object
+     * @param string $class   The folder class to fetch (Calendar, Contacts etc.)
+     *
+     * @return mixed  Either an array of folder data || false
+     */
+    abstract public function getFolderData($device, $class);
+
+    /**
      * Get all items that have changed since the last sync time
      *
      * @param integer $flags
@@ -517,27 +553,28 @@ abstract class Horde_ActiveSync_State_Base
     /**
      * Obtain the device object.
      *
-     * @param string $devId
+     * @param object $device
+     * @param string $user
      *
      * @return StdClass
      */
-    abstract public function getDeviceInfo($devId);
+    abstract public function getDeviceInfo($device, $user);
 
     /**
      * Check that a given device id is known to the server. This is regardless
      * of Provisioning status.
      *
-     * @param string $devId
+     * @param string $devId  The device id to check
+     * @param string $user   The device should be owned by this user.
      *
      * @return boolean
      */
-    abstract public function deviceExists($devId);
+    abstract public function deviceExists($devId, $user);
 
     /**
      * Set new device info
      *
-     * @param string $devId   The device id.
-     * @param StdClass $data  The device information
+     * @param object $device  The device information
      *
      * @return boolean
      */
@@ -577,13 +614,11 @@ abstract class Horde_ActiveSync_State_Base
     abstract public function listDevices();
 
     /**
-     * Get the last time a particular device issued a SYNC request.
-     *
-     * @param string $devId  The device id
+     * Get the last time the currently loaded device issued a SYNC request.
      *
      * @return integer  The timestamp of the last sync, regardless of collection
      * @throws Horde_ActiveSync_Exception
      */
-    abstract public function getLastSyncTimestamp($devId);
+    abstract public function getLastSyncTimestamp();
 
 }
\ No newline at end of file
index 8752834..e5051d8 100644 (file)
@@ -337,27 +337,21 @@ class Horde_ActiveSync_State_File extends Horde_ActiveSync_State_Base
     /**
      * Obtain the device object.
      *
-     * @param string $devId
+     * @param string $devId  The device id to obtain
+     * @param string $user   The user account to use
      *
-     * @return StdClass
+     * @return object  The device info object
+     * @throws Horde_ActiveSync_Exception
      */
-    public function getDeviceInfo($devId)
+    public function getDeviceInfo($devId, $user)
     {
         $this->_devId = $devId;
-        $file = $this->_stateDir . '/' . $this->_backend->getUser() . '/info-' . $devId;
+        $file = $this->_stateDir . '/' . $user . '/info-' . $devId;
         if (file_exists($file)) {
             return unserialize(file_get_contents($file));
+        } else {
+            throw new Horde_ActiveSync_Exception('Device not found.');
         }
-
-        /* Default structure */
-        $device = new StdClass();
-        $device->policykey = 0;
-        $device->rwstatus = Horde_ActiveSync::RWSTATUS_NA;
-        $device->deviceType = '';
-        $device->userAgent = '';
-        $device->id = $devId;
-
-        return $device;
     }
 
     /**
@@ -381,12 +375,13 @@ class Horde_ActiveSync_State_File extends Horde_ActiveSync_State_Base
      * of Provisioning status.
      *
      * @param string $devId
+     * @param string $user
      *
      * @return boolean
      */
-    public function deviceExists($devId)
+    public function deviceExists($devId, $user)
     {
-        return file_exists($this->_stateDir . '/' . $this->_backend->getUser() . '/info-' . $devId);
+        return file_exists($this->_stateDir . '/' . $user . '/info-' . $devId);
     }
 
     /**
@@ -609,7 +604,7 @@ class Horde_ActiveSync_State_File extends Horde_ActiveSync_State_Base
      * @return integer  The timestamp of the last sync, regardless of collection
      * @throws Horde_ActiveSync_Exception
      */
-    public function getLastSyncTimestamp($devId)
+    public function getLastSyncTimestamp()
     {
         throw new Horde_ActiveSync_Exception('Not Implemented');
     }
index 373390f..553bcc0 100644 (file)
@@ -11,6 +11,7 @@
  *                    contains the current folder state on the PIM.
  *        sync_devid: The device id.
  *        sync_folderid: The folder id for this sync.
+ *        sync_user:     The user for this synckey
  *
  *    syncMapTable (horde_activesync_map):
  *        message_uid    - The server uid for the object
  *        sync_key       - The syncKey that was current at the time the change
  *                         was received.
  *        sync_devid     - The device id this change was done on.
+ *        sync_user      - The user that initiated the change.
  *
  *    syncDeviceTable (horde_activesync_device:
  *        device_id      - The unique id for this device
  *        device_type    - The device type the PIM identifies itself with
  *        device_agent   - The user agent string sent by the device
- *        device_ping    - The device's current PING state information.
  *        device_policykey  - The current policykey for this device
  *        device_rwstatus   - The current remote wipe status for this device
- *        device_user       - The user this device belongs to.
+ *
+ *    syncUsersTable (horde_activesync_device_users:
+ *        device_user
+ *        device_id
+ *        device_ping
+ *        device_folders
  * </pre>
  *
  * Copyright 2010 The Horde Project (http://www.horde.org)
@@ -69,6 +75,7 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
     protected $_syncStateTable;
     protected $_syncMapTable;
     protected $_syncDeviceTable;
+    protected $_syncUsersTable;
 
     /**
      * Const'r
@@ -80,6 +87,7 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
      *      'syncMapTable'      - Name of table for remembering what changes
      *                            are due to PIM import so we don't mirror the
      *                            changes back to the PIM on next Sync
+     *      'syncUsersTable'    - Name of table for mapping users to devices.
      *
      * @return Horde_ActiveSync_StateMachine_File
      */
@@ -93,6 +101,7 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
         $this->_syncStateTable = $params['statetable'];
         $this->_syncMapTable = $params['maptable'];
         $this->_syncDeviceTable = $params['devicetable'];
+        $this->_syncUsersTable = $params['userstable'];
         $this->_db = $params['db'];
     }
 
@@ -194,7 +203,7 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
 
         /*  Update state table to remember this last synctime and key */
         $sql = 'INSERT INTO ' . $this->_syncStateTable
-            . ' (sync_key, sync_data, sync_devid, sync_time, sync_folderid) VALUES (?, ?, ?, ?, ?)';
+            . ' (sync_key, sync_data, sync_devid, sync_time, sync_folderid, sync_user) VALUES (?, ?, ?, ?, ?, ?)';
 
         /* Remember any left over changes */
         if ($this->_type == 'foldersync') {
@@ -209,7 +218,8 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
                         $data,
                         $this->_devId,
                         $this->_thisSyncTS,
-                        !empty($this->_collection['id']) ? $this->_collection['id'] : 'foldersync');
+                        !empty($this->_collection['id']) ? $this->_collection['id'] : 'foldersync',
+                        $this->_deviceInfo->user);
         try {
             $this->_db->insert($sql, $params);
         } catch (Horde_Db_Exception $e) {
@@ -282,13 +292,13 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
      * activesync versions that use GETHIERARCHY requests to get the folder info
      * instead of maintaining the folder state with FOLDERSYNC requests.
      *
-     * @param string $devId  The device Id
-     * @param array $folders The folder data
+     * @param object $device  The device object
+     * @param array $folders  The folder data
      *
      * @return boolean
      * @throws Horde_ActiveSync_Exception
      */
-    public function setFolderData($devId, $folders)
+    public function setFolderData($device, $folders)
     {
         if (!is_array($folders) || empty ($folders)) {
             return false;
@@ -316,10 +326,10 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
             $unique_folders[Horde_ActiveSync::FOLDER_TYPE_CONTACT] = Horde_ActiveSync::FOLDER_TYPE_DUMMY;
         }
 
-        /* Storage to SQL? */
-        $sql = 'UPDATE ' . $this->_syncDeviceTable . ' SET device_folders = ? WHERE device_id = ?';
+        /* Store it*/
+        $sql = 'UPDATE ' . $this->_syncUsersTable . ' SET device_folders = ? WHERE device_id = ? AND device_user = ?';
         try {
-            return $this->_db->update($sql, array(serialize($folders), $devId));
+            return $this->_db->update($sql, array(serialize($folders), $device->id, $device->user));
         } catch (Horde_Db_Exception $e) {
             throw new Horde_ActiveSync_Exception($e);
         }
@@ -328,16 +338,16 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
     /**
      * Get the folder data for a specific device
      *
-     * @param string $devId  The device id
-     * @param string $class  The folder class to fetch (Calendar, Contacts etc.)
+     * @param object $device  The device object
+     * @param string $class   The folder class to fetch (Calendar, Contacts etc.)
      *
      * @return mixed  Either an array of folder data || false
      */
-    public function getFolderData($devId, $class)
+    public function getFolderData($device, $class)
     {
-        $sql = 'SELECT device_folders FROM ' . $this->_syncDeviceTable . ' WHERE device_id = ?';
+        $sql = 'SELECT device_folders FROM ' . $this->_syncUsersTable . ' WHERE device_id = ? AND device_user = ?';
         try {
-            $folders = $this->_db->selectValue($sql, array($devId));
+            $folders = $this->_db->selectValue($sql, array($device->id, $device->user));
         } catch (Horde_Db_Exception $e) {
             throw new Horde_ActiveSync_Exception($e);
         }
@@ -382,12 +392,12 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
      *
      * @return The $collection array
      */
-    public function initPingState($devId)
+    public function initPingState($device)
     {
         /* This would normally already be loaded by getDeviceInfo() but we
          * should verify we have the correct device loaded etc... */
-         if (!isset($this->_pingState) || $this->_devId !== $devId) {
-             $this->getDeviceInfo($devId);
+         if (!isset($this->_pingState) || $this->_devId !== $device->id) {
+             throw new Horde_ActiveSync_Exception('Device not loaded');
          }
 
          /* Need to get the last sync time for this collection */
@@ -398,22 +408,26 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
      * Obtain the device object. For this driver, we also store the PING data
      * in the device table.
      *
-     * @param string $devId
+     * @param string $devId   The device id to obtain
+     * @param string $user    The user to retrieve user-specific device info for
      *
-     * @return StdClass
+     * @return object  The device obejct
+     * @throws Horde_ActiveSync_Exception
      */
-    public function getDeviceInfo($devId)
+    public function getDeviceInfo($devId, $user)
     {
+        //@TODO - combine _devId and _deviceInfo
         /* See if we have it already */
         if ($this->_devId == $devId && !empty($this->_deviceInfo)) {
             return $this->_deviceInfo;
         }
 
         $this->_devId = $devId;
-        $query = 'SELECT device_type, device_agent, device_ping, device_policykey, device_rwstatus, device_user, device_supported FROM '
-            . $this->_syncDeviceTable . ' WHERE device_id = ?';
+        $query = 'SELECT device_type, device_agent, device_ping, device_policykey, device_rwstatus, device_supported FROM '
+            . $this->_syncDeviceTable . ' d INNER JOIN ' . $this->_syncUsersTable . ' u ON d.device_id = u.device_id WHERE u.device_id = ? AND u.device_user = ?';
         try {
-            $result = $this->_db->selectOne($query, array($devId));
+            $this->_logger->debug('SQL QUERY: ' . $query . ' VALUES: ' . $devId . ' ' . $user);
+            $result = $this->_db->selectOne($query, array($devId, $user));
         } catch (Horde_Db_Exception $e) {
             throw new Horde_ActiveSync_Exception($e);
         }
@@ -424,7 +438,7 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
             $this->_deviceInfo->deviceType = $result['device_type'];
             $this->_deviceInfo->userAgent = $result['device_agent'];
             $this->_deviceInfo->id = $devId;
-            $this->_deviceInfo->user = $result['device_user'];
+            $this->_deviceInfo->user = $user;
             $this->_deviceInfo->supported = unserialize($result['device_supported']);
             if ($result['device_ping']) {
                 $this->_pingState = empty($result['device_ping']) ? array() : unserialize($result['device_ping']);
@@ -432,15 +446,7 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
                 $this->resetPingState();
             }
         } else {
-            /* Default structure */
-            $this->_deviceInfo->policykey = 0;
-            $this->_deviceInfo->rwstatus = Horde_ActiveSync::RWSTATUS_NA;
-            $this->_deviceInfo->deviceType = '';
-            $this->_deviceInfo->userAgent = '';
-            $this->_deviceInfo->id = $devId;
-            $this->_deviceInfo->user = $this->_backend->getUser();
-            $this->setDeviceInfo($this->_deviceInfo);
-            $this->resetPingState();
+            throw new Horde_ActiveSync_Exception('Device not found.');
         }
 
         return $this->_deviceInfo;
@@ -449,34 +455,55 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
     /**
      * Set new device info
      *
-     * @param StdClass $data  The device information
+     * @param object $data  The device information
      *
      * @return boolean
      */
     public function setDeviceInfo($data)
     {
-        /* Delete the old entry, just in case */
-        $this->_deviceInfo = $data;
+        /* Make sure we have the device entry */
         try {
-            $query = 'DELETE FROM ' . $this->_syncDeviceTable . ' WHERE device_id = ?';
-            $this->_db->execute($query, array($data->id));
-
-            $query = 'INSERT INTO ' . $this->_syncDeviceTable
-                . '(device_type, device_agent, device_ping, device_policykey, device_rwstatus, device_id, device_user, device_supported)'
-                . ' VALUES(?, ?, ?, ?, ?, ?, ?, ?)';
-
-            $values = array($data->deviceType,
-                            $data->userAgent,
-                            '',
-                            $data->policykey,
-                            $data->rwstatus,
-                            $data->id,
-                            $data->user,
-                            (!empty($data->supported) ? serialize($data->supported) : ''));
+            if (!$this->deviceExists($data->id)) {
+                $this->_logger->debug('[' . $data->id . '] Device entry does not exist, creating it.');
+                $query = 'INSERT INTO ' . $this->_syncDeviceTable
+                    . ' (device_type, device_agent, device_policykey, device_rwstatus, device_id, device_supported)'
+                    . ' VALUES(?, ?, ?, ?, ?, ?)';
+                $values = array(
+                    $data->deviceType,
+                    $data->userAgent,
+                    $data->policykey,
+                    $data->rwstatus,
+                    $data->id,
+                    (!empty($data->supported) ? serialize($data->supported) : '')
+                );
+                $this->_db->execute($query, $values);
+            }
+        } catch(Horde_Db_Exception $e) {
+            throw new Horde_ActiveSync_Exception($e);
+        }
 
-            $this->_devId = $data->id;
+        $this->_deviceInfo = $data;
 
-            return $this->_db->insert($query, $values);
+        /* See if we have the user already also */
+        try {
+            $query = 'SELECT COUNT(*) FROM ' . $this->_syncUsersTable . ' WHERE device_id = ? AND device_user = ?';
+            $cnt = $this->_db->selectValue($query, array($data->id, $data->user));
+            if (!$cnt) {
+                $this->_logger->debug('[' . $data->id . '] Device entry does not exist for user ' . $data->user . ', creating it.');
+                $query = 'INSERT INTO ' . $this->_syncUsersTable
+                    . ' (device_ping, device_id, device_user)'
+                    . ' VALUES(?, ?, ?)';
+
+                $values = array(
+                    '',
+                    $data->id,
+                    $data->user
+                );
+                $this->_devId = $data->id;
+                return $this->_db->insert($query, $values);
+            } else {
+                return true;
+            }
         } catch (Horde_Db_Exception $e) {
             throw new Horde_ActiveSync_Exception($e);
         }
@@ -484,17 +511,27 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
 
     /**
      * Check that a given device id is known to the server. This is regardless
-     * of Provisioning status.
+     * of Provisioning status. If $user is provided, checks that the device
+     * is attached to the provided username.
      *
-     * @param string $devId
+     * @param string $devId  The device id to check.
+     * @param string $user   The device should be owned by this user.
      *
      * @return boolean
      */
-    public function deviceExists($devId)
+    public function deviceExists($devId, $user = null)
     {
-        $query = 'SELECT COUNT(*) FROM ' . $this->_syncDeviceTable . ' WHERE device_id = ?';
+        if (!empty($user)) {
+            $query = 'SELECT COUNT(*) FROM ' . $this->_syncDeviceTable . ' d INNER JOIN '
+                . $this->_syncUsersTable . ' u ON d.device_id = u.device_id WHERE '
+                . ' d.device_id = ? AND u.device_user = ?';
+            $values = array($devId, $user);
+        } else {
+            $query = 'SELECT COUNT(*) FROM ' . $this->_syncDeviceTable . ' WHERE device_id = ?';
+            $values = array($devId);
+        }
         try {
-            return $this->_db->selectValue($query, array($devId));
+            return $this->_db->selectValue($query, $values);
         } catch (Horde_Db_Exception $e) {
             throw new Horde_ActiveSync_Exception($e);
         }
@@ -508,32 +545,38 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
      */
     public function listDevices($user = null)
     {
-        $sql = 'SELECT * from ' . $this->_syncDeviceTable;
-        $values = array();
-        if ($user) {
-            $sql .= ' WHERE device_user = ?';
-            $values[] = $user;
+        if (!empty($user)) {
+            $query = 'SELECT d.device_id device_id, device_type, device_agent,'
+                . ' device_policykey, device_rwstatus, device_user FROM '
+                . $this->_syncDeviceTable . ' d INNER JOIN ' . $this->_syncUsersTable
+                . ' u ON d.device_id = u.device_id WHERE u.device_user = ?';
+            $values = array($user);
+        } else {
+            $query = 'SELECT * from ' . $this->_syncDeviceTable;
+            $values = array();
         }
         try {
-            return $this->_db->selectAll($sql, $values);
+            return $this->_db->selectAll($query, $values);
         } catch (Horde_Db_Exception $e) {
             throw new Horde_ActiveSync_Exception($e);
         }
     }
 
     /**
-     * Get the last time a particular device issued a SYNC request.
-     *
-     * @param string $devId  The device id
+     * Get the last time the loaded device issued a SYNC request.
      *
      * @return integer  The timestamp of the last sync, regardless of collection
      * @throws Horde_ActiveSync_Exception
      */
-    public function getLastSyncTimestamp($devId)
+    public function getLastSyncTimestamp()
     {
-        $sql = 'SELECT sync_time FROM ' . $this->_syncStateTable . ' WHERE sync_devid = ?';
+        if (empty($this->_deviceInfo)) {
+            throw new Horde_ActiveSync_Exception('Device not loaded.');
+        }
+
+        $sql = 'SELECT MAX(sync_time) FROM ' . $this->_syncStateTable . ' WHERE sync_devid = ? AND sync_user = ?';
         try {
-            return $this->_db->selectValue($sql, array($devId));
+            return $this->_db->selectValue($sql, array($this->_devId, $this->_deviceInfo->user));
         } catch (Horde_Db_Exception $e) {
             throw new Horde_ActiveSync_Exception($e);
         }
@@ -607,10 +650,10 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
         }
 
         $state = serialize(array('lifetime' => $this->_pingState['lifetime'], 'collections' => $this->_pingState['collections']));
-        $query = 'UPDATE ' . $this->_syncDeviceTable . ' SET device_ping = ? WHERE device_id = ?';
+        $query = 'UPDATE ' . $this->_syncUsersTable . ' SET device_ping = ? WHERE device_id = ? AND device_user = ?';
 
         try {
-            return $this->_db->update($query, array($state, $this->_devId));
+            return $this->_db->update($query, array($state, $this->_devId, $this->_deviceInfo->user));
         } catch (Horde_Db_Exception $e) {
             throw new Horde_ActiveSync_Exception($e);
         }
@@ -718,8 +761,8 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
      */
     public function setPolicyKey($devId, $key)
     {
-        if (empty($this->_deviceInfo)) {
-            $this->getDeviceInfo($devId);
+        if (empty($this->_deviceInfo) || $devId != $this->_deviceInfo->id) {
+            throw new Horde_ActiveSync_Exception('Device not loaded');
         }
 
         $query = 'UPDATE ' . $this->_syncDeviceTable . ' SET device_policykey = ? WHERE device_id = ?';
@@ -741,15 +784,15 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
      */
     public function setDeviceRWStatus($devId, $status)
     {
-        if (empty($this->_deviceInfo)) {
-            $this->getDeviceInfo($devId);
+        if (empty($this->_deviceInfo) || $devId != $this->_deviceInfo->id) {
+            throw new Horde_ActiveSync_Exception('Device not loaded');
         }
 
         $query = 'UPDATE ' . $this->_syncDeviceTable . ' SET device_rwstatus = ?';
         $values = array($status);
 
         if ($status == Horde_ActiveSync::RWSTATUS_PENDING) {
-            /* Need to clear the policykey to force a provision */
+            /* Need to clear the policykey to force a PROVISION */
             $query .= ',device_policykey = ?';
             $values[] = 0;
         }
@@ -766,20 +809,30 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
      * Explicitly remove a state from storage.
      *
      * @param string $synckey  The specific state to remove
-     * @param string $devId    Remove all information for this device (ignores synckey)
+     * @param string $devId    Remove all information for this device.
+     * @param string $user     When removing device info, restrict to removing
+     *                         data for this user only.
      *
      * @throws Horde_ActiveSyncException
      */
-    public function removeState($synckey = null, $devId = null)
+    public function removeState($synckey = null, $devId = null, $user = null)
     {
         $state_query = 'DELETE FROM ' . $this->_syncStateTable . ' WHERE';
         $map_query = 'DELETE FROM ' . $this->_syncMapTable . ' WHERE';
-        if ($devId) {
+        if ($devId && $user) {
+            $state_query .= ' sync_devid = ? AND sync_user = ?';
+            $map_query .= ' sync_devid = ? AND sync_user = ?';
+            $user_query = 'DELETE FROM ' . $this->_syncUsersTable . ' WHERE device_id = ? AND device_user = ?';
+            $values = array($devId, $user);
+            $this->_logger->debug('[' . $devId . '] Removing device state for user ' . $user . '.');
+        } elseif ($devId){
             $state_query .= ' sync_devid = ?';
             $map_query .= ' sync_devid = ?';
+            $user_query = 'DELETE FROM ' . $this->_syncUsersTable . ' WHERE device_id = ?';
             $device_query = 'DELETE FROM ' . $this->_syncDeviceTable . ' WHERE device_id = ?';
             $values = array($devId);
-            $this->_logger->debug('[' . $devId . '] Removing device state.');
+            $this->_logger->debug('[' . $devId . '] Removing device state for user ' . $user . '.');
+
         } else {
             $state_query .= ' sync_key = ?';
             $map_query .= ' sync_key = ?';
@@ -790,8 +843,19 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
         try {
             $this->_db->delete($state_query, $values);
             $this->_db->delete($map_query, $values);
+            if (!empty($user_query)) {
+                $this->_db->delete($user_query, $values);
+            }
             if (!empty($device_query)) {
                 $this->_db->delete($device_query, $values);
+            } elseif (!empty($user_query)) {
+                /* If there was a user_deletion, check if we should remove the
+                 * device entry as well */
+                $sql = 'SELECT COUNT(*) FROM ' . $this->_syncUsersTable . ' WHERE device_id = ?';
+                if (!$this->_db->selectValue($sql, array($devId))) {
+                    $query = 'DELETE FROM ' . $this->_syncDeviceTable . ' WHERE device_id = ?';
+                    $this->_db->delete($query, array($devId));
+                }
             }
         } catch (Horde_Db_Exception $e) {
             throw new Horde_ActiveSync_Exception($e);
@@ -807,9 +871,9 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
      */
     protected function _getPIMChangeTS($uid)
     {
-        $sql = 'SELECT sync_modtime FROM ' . $this->_syncMapTable . ' WHERE message_uid = ? AND sync_devid = ?';
+        $sql = 'SELECT sync_modtime FROM ' . $this->_syncMapTable . ' WHERE message_uid = ? AND sync_devid = ? AND sync_user = ?';
         try {
-            return $this->_db->selectValue($sql, array($uid, $this->_devId));
+            return $this->_db->selectValue($sql, array($uid, $this->_devId, $this->_deviceInfo->user));
         } catch (Horde_Db_Exception $e) {
             throw new Horde_ActiveSync_Exception($e);
         }
@@ -818,7 +882,7 @@ class Horde_ActiveSync_State_History extends Horde_ActiveSync_State_Base
     /**
      * Check for the existence of ANY entries in the map table for this device.
      * An extra database query for each sync, but the payoff is that we avoid
-     * having to state every message change we send to the PIM if there are no
+     * having to stat every message change we send to the PIM if there are no
      * PIM generated changes for this sync period.
      *
      * @return boolean
index 5f87fc0..b090abb 100644 (file)
@@ -226,9 +226,13 @@ class Horde_ActiveSync_HordeDriverTest extends Horde_Test_Case
         /* Fixtures - don't really need data, since the change is not actually done */
         $message = new Horde_ActiveSync_Message_Contact();
 
+        /* Mock device object */
+        $device = new stdClass();
+        $device->supported = array();
+
         /* Try adding a new contact */
         try {
-            $results = $driver->ChangeMessage(Horde_ActiveSync_Driver_Horde::CONTACTS_FOLDER, 0, $message);
+            $results = $driver->ChangeMessage(Horde_ActiveSync_Driver_Horde::CONTACTS_FOLDER, 0, $message, $device);
         } catch (Horde_ActiveSync_Exception $e) {
             $this->fail($e->getMessage());
         }
@@ -237,7 +241,7 @@ class Horde_ActiveSync_HordeDriverTest extends Horde_Test_Case
 
        /* Try editing a contact */
         try {
-            $results = $driver->ChangeMessage(Horde_ActiveSync_Driver_Horde::CONTACTS_FOLDER, 'localhost@123.123', $message);
+            $results = $driver->ChangeMessage(Horde_ActiveSync_Driver_Horde::CONTACTS_FOLDER, 'localhost@123.123', $message, $device);
         } catch (Horde_ActiveSync_Exception $e) {
             $this->fail($e->getMessage());
         }
@@ -247,7 +251,7 @@ class Horde_ActiveSync_HordeDriverTest extends Horde_Test_Case
         /* Try adding a new appointment */
         $message = new Horde_ActiveSync_Message_Appointment();
         try {
-            $results = $driver->ChangeMessage(Horde_ActiveSync_Driver_Horde::APPOINTMENTS_FOLDER, 0, $message);
+            $results = $driver->ChangeMessage(Horde_ActiveSync_Driver_Horde::APPOINTMENTS_FOLDER, 0, $message, $device);
         } catch (Horde_ActiveSync_Exception $e) {
             $this->fail($e->getMessage());
         }
@@ -256,7 +260,7 @@ class Horde_ActiveSync_HordeDriverTest extends Horde_Test_Case
 
        /* Try editing an appointment */
         try {
-            $results = $driver->ChangeMessage(Horde_ActiveSync_Driver_Horde::APPOINTMENTS_FOLDER, 'localhost@123.123', $message);
+            $results = $driver->ChangeMessage(Horde_ActiveSync_Driver_Horde::APPOINTMENTS_FOLDER, 'localhost@123.123', $message, $device);
         } catch (Horde_ActiveSync_Exception $e) {
             $this->fail($e->getMessage());
         }
@@ -266,7 +270,7 @@ class Horde_ActiveSync_HordeDriverTest extends Horde_Test_Case
         /* Try adding a new task */
         $message = new Horde_ActiveSync_Message_Task();
         try {
-            $results = $driver->ChangeMessage(Horde_ActiveSync_Driver_Horde::TASKS_FOLDER, 0, $message);
+            $results = $driver->ChangeMessage(Horde_ActiveSync_Driver_Horde::TASKS_FOLDER, 0, $message, $device);
         } catch (Horde_ActiveSync_Exception $e) {
             $this->fail($e->getMessage());
         }
@@ -275,7 +279,7 @@ class Horde_ActiveSync_HordeDriverTest extends Horde_Test_Case
 
        /* Try editing an appointment */
         try {
-            $results = $driver->ChangeMessage(Horde_ActiveSync_Driver_Horde::TASKS_FOLDER, 'localhost@123.123', $message);
+            $results = $driver->ChangeMessage(Horde_ActiveSync_Driver_Horde::TASKS_FOLDER, 'localhost@123.123', $message, $device);
         } catch (Horde_ActiveSync_Exception $e) {
             $this->fail($e->getMessage());
         }
index a17c838..47e43e3 100644 (file)
           data:">horde_activesync_state</configstring>
           <configstring name="maptable" desc="Name of table to hold mapping data
           for device-sent changes.">horde_activesync_map</configstring>
+          <configstring name="userstable" desc="Name of table to hold user-device
+          data:">horde_activesync_device_users</configstring>
          </configsection>
        </case>
        <case name="file" desc="Generic file based driver">
index 1deed08..2826a79 100644 (file)
@@ -416,12 +416,14 @@ class Horde_Prefs_Ui
         $t->setOption('gettext', true);
         $selfurl = $ui->selfUrl();
         $t->set('reset', $selfurl->copy()->add('reset', 1));
+        $t->set('username', Horde_Auth::getAuth());
         $devices = $stateMachine->listDevices(Horde_Auth::getAuth());
         $devs = array();
         $i = 1;
         foreach ($devices as $device) {
             $device['class'] = fmod($i++, 2) ? 'rowOdd' : 'rowEven';
-            $ts = $stateMachine->getLastSyncTimestamp($device['device_id']);
+            $stateMachine->getDeviceInfo($device['device_id'], Horde_Auth::getAuth());
+            $ts = $stateMachine->getLastSyncTimestamp();
             $device['ts'] = empty($ts) ? _("None") : strftime($GLOBALS['prefs']->getValue('date_format') . ' %H:%M', $ts);
             switch ($device['device_rwstatus']) {
             case Horde_ActiveSync::RWSTATUS_PENDING:
@@ -702,11 +704,11 @@ class Horde_Prefs_Ui
         } elseif ($ui->vars->reset) {
             $devices = $stateMachine->listDevices(Horde_Auth::getAuth());
             foreach ($devices as $device) {
-                $stateMachine->removeState(null, $device['device_id']);
+                $stateMachine->removeState(null, $device['device_id'], $ui->vars->removeuser);
             }
             $GLOBALS['notification']->push(_("All state removed for your devices. They will resynchronize next time they connect to the server."));
         } elseif ($ui->vars->removedevice) {
-            $stateMachine->removeState(null, $ui->vars->removedevice);
+            $stateMachine->removeState(null, $ui->vars->removedevice, $ui->vars->removeuser);
         }
     }
 
index 3b88ce9..69f5e2d 100644 (file)
@@ -236,6 +236,7 @@ CREATE TABLE horde_activesync_state (
     sync_data          TEXT,
     sync_devid         VARCHAR(255),
     sync_folderid      VARCHAR(255),
+    sync_user          VARCHAR(255) NOT NULL,
 --
     PRIMARY KEY (sync_key)
 );
@@ -250,10 +251,12 @@ CREATE TABLE horde_activesync_map (
     sync_modtime       INT,
     sync_key           VARCHAR(255) NOT NULL,
     sync_devid         VARCHAR(255) NOT NULL,
-    sync_folderid      VARCHAR(255) NOT NULL
+    sync_folderid      VARCHAR(255) NOT NULL,
+    sync_user          VARCHAR(255) NOT NULL
 );
 GO
 
+CREATE INDEX activesync_map_user_idx ON horde_activesync_map (sync_user);
 CREATE INDEX activesync_map_devid_idx ON horde_activesync_map (sync_devid);
 CREATE INDEX activesync_map_message_idx ON horde_activesync_map (message_uid);
 GO
@@ -262,17 +265,23 @@ CREATE TABLE horde_activesync_device (
     device_id         VARCHAR(255) NOT NULL,
     device_type       VARCHAR(255) NOT NULL,
     device_agent      VARCHAR(255) NOT NULL,
-    device_ping       TEXT,
     device_supported  TEXT,
     device_policykey  BIGINT DEFAULT 0,
     device_rwstatus   INT,
-    device_folders    TEXT,
-    device_user       VARCHAR(255),
-
 --
     PRIMARY KEY (device_id)
 );
 GO
 
-CREATE INDEX activesync_device_user_idx ON horde_activesync_device (device_user);
+CREATE TABLE horde_activesync_device_users (
+    device_id         VARCHAR(255) NOT NULL,
+    device_user       VARCHAR(255) NOT NULL,
+    device_ping       TEXT,
+    device_folders    TEXT,
+--
+    PRIMARY KEY (device_id, device_user)
+);
 GO
+
+CREATE INDEX activesync_device_users_idx ON horde_activesync_device_users (device_user);
+GO
\ No newline at end of file
index c18e95e..6812d0d 100644 (file)
@@ -259,6 +259,7 @@ CREATE TABLE horde_activesync_state (
     sync_data          TEXT,
     sync_devid         VARCHAR(255),
     sync_folderid      VARCHAR(255),
+    sync_user          VARCHAR(255) NOT NULL,
 --
     PRIMARY KEY (sync_key)
 );
@@ -271,27 +272,35 @@ CREATE TABLE horde_activesync_map (
     sync_modtime       INTEGER,
     sync_key           VARCHAR(255) NOT NULL,
     sync_devid         VARCHAR(255) NOT NULL,
-    sync_folderid      VARCHAR(255) NOT NULL
+    sync_folderid      VARCHAR(255) NOT NULL,
+    sync_user          VARCHAR(255) NOT NULL
 );
 
 CREATE INDEX activesync_map_devid_idx ON horde_activesync_map (sync_devid);
 CREATE INDEX activesync_map_message_idx ON horde_activesync_map (message_uid);
+CREATE INDEX activesync_map_user_idx ON horde_activesync_map (sync_user);
+
 
 CREATE TABLE horde_activesync_device (
     device_id         VARCHAR(255) NOT NULL,
     device_type       VARCHAR(255) NOT NULL,
     device_agent      VARCHAR(255) NOT NULL,
-    device_ping       TEXT,
     device_supported  TEXT,
     device_policykey  BIGINT DEFAULT 0,
     device_rwstatus   INTEGER,
-    device_folders    TEXT,
-    device_user       VARCHAR(255),
-
 --
     PRIMARY KEY (device_id)
 );
 
-CREATE INDEX activesync_device_user_idx ON horde_activesync_device (device_user);
+CREATE TABLE horde_activesync_device_users (
+    device_id         VARCHAR(255) NOT NULL,
+    device_user       VARCHAR(255) NOT NULL,
+    device_ping       TEXT,
+    device_folders    TEXT,
+--
+    PRIMARY KEY (device_id, device_user)
+);
+
+CREATE INDEX activesync_device_users_idx ON horde_activesync_device_users (device_user);
 
 -- Done.
index 67ca21b..cfca2ae 100644 (file)
@@ -284,6 +284,7 @@ CREATE TABLE horde_activesync_state (
     sync_data          CLOB,
     sync_devid         VARCHAR2(255),
     sync_folderid      VARCHAR2(255),
+    sync_user          VARCHAR2(255) NOT NULL,
 --
     PRIMARY KEY (sync_key)
 );
@@ -296,26 +297,34 @@ CREATE TABLE horde_activesync_map (
     sync_modtime       NUMBER(16),
     sync_key           VARCHAR2(255) NOT NULL,
     sync_devid         VARCHAR2(255) NOT NULL,
-    sync_folderid      VARCHAR2(255) NOT NULL
+    sync_folderid      VARCHAR2(255) NOT NULL,
+    sync_user          VARCHAR2(255) NOT NULL
 );
 
 CREATE INDEX activesync_map_devid_idx ON horde_activesync_map (sync_devid);
 CREATE INDEX activesync_map_message_idx ON horde_activesync_map (message_uid);
+CREATE INDEX activesync_map_user_idx ON horde_activesync_map (sync_user);
+
 
 CREATE TABLE horde_activesync_device (
     device_id         VARCHAR2(255) NOT NULL,
     device_type       VARCHAR2(255) NOT NULL,
     device_agent      VARCHAR2(255) NOT NULL,
-    device_ping       CLOB,
     device_supported  CLOB,
     device_policykey  NUMBER(16) DEFAULT 0,
     device_rwstatus   NUMBER(8),
-    device_folders    CLOB,
-    device_user       VARCHAR2(255),
 --
     PRIMARY KEY (device_id)
 );
 
-CREATE INDEX activesync_device_user_idx ON horde_activesync_device (device_user);
+CREATE TABLE horde_activesync_device_users (
+    device_id         VARCHAR2(255) NOT NULL,
+    device_user       VARCHAR2(255) NOT NULL,
+    device_ping       CLOB,
+    device_folders    CLOB,
+--
+    PRIMARY KEY (device_id, device_user)
+);
+CREATE INDEX activesync_device_users_idx ON horde_activesync_device_users (device_user);
 
 exit
index 9a3dd65..229b8ca 100644 (file)
@@ -220,6 +220,7 @@ CREATE TABLE horde_activesync_state (
     sync_data          TEXT,
     sync_devid         VARCHAR(255),
     sync_folderid      VARCHAR(255),
+    sync_user          VARCHAR(255) NOT NULL,
 --
     PRIMARY KEY (sync_key)
 );
@@ -232,27 +233,33 @@ CREATE TABLE horde_activesync_map (
     sync_modtime       INT,
     sync_key           VARCHAR(255) NOT NULL,
     sync_devid         VARCHAR(255) NOT NULL,
-    sync_folderid      VARCHAR(255) NOT NULL
+    sync_folderid      VARCHAR(255) NOT NULL,
+    sync_user          VARCHAR(255) NOT NULL
 );
 
 CREATE INDEX activesync_map_devid_idx ON horde_activesync_map (sync_devid);
 CREATE INDEX activesync_map_message_idx ON horde_activesync_map (message_uid);
+CREATE INDEX activesync_map_user_idx ON horde_activesync_map (sync_user);
 
 CREATE TABLE horde_activesync_device (
     device_id         VARCHAR(255) NOT NULL,
     device_type       VARCHAR(255) NOT NULL,
     device_agent      VARCHAR(255) NOT NULL,
-    device_ping       TEXT,
     device_supported  TEXT,
     device_policykey  BIGINT DEFAULT 0,
     device_rwstatus   INT,
-    device_folders    TEXT,
-    device_user       VARCHAR(255),
-
 --
     PRIMARY KEY (device_id)
 );
 
-CREATE INDEX activesync_device_user_idx ON horde_activesync_device (device_user);
+CREATE TABLE horde_activesync_device_users (
+    device_id         VARCHAR(255) NOT NULL,
+    device_user       VARCHAR(255) NOT NULL,
+    device_ping       TEXT,
+    device_folders    TEXT,
+--
+    PRIMARY KEY (device_id, device_user)
+);
+CREATE INDEX activesync_device_users_idx ON horde_activesync_device_users (device_user);
 
 COMMIT;
index aca818f..012d75a 100644 (file)
@@ -227,6 +227,7 @@ CREATE TABLE horde_activesync_state (
     sync_data          TEXT,
     sync_devid         VARCHAR(255),
     sync_folderid      VARCHAR(255),
+    sync_user          VARCHAR(255) NOT NULL,
 --
     PRIMARY KEY (sync_key)
 );
@@ -239,25 +240,31 @@ CREATE TABLE horde_activesync_map (
     sync_modtime       INTEGER,
     sync_key           VARCHAR(255) NOT NULL,
     sync_devid         VARCHAR(255) NOT NULL,
-    sync_folderid      VARCHAR(255) NOT NULL
+    sync_folderid      VARCHAR(255) NOT NULL,
+    sync_user          VARCHAR(255) NOT NULL
 );
 
 CREATE INDEX activesync_map_devid_idx ON horde_activesync_map (sync_devid);
 CREATE INDEX activesync_map_message_idx ON horde_activesync_map (message_uid);
+CREATE INDEX activesync_map_user_idx ON horde_activesync_map (sync_user);
 
 CREATE TABLE horde_activesync_device (
     device_id         VARCHAR(255) NOT NULL,
     device_type       VARCHAR(255) NOT NULL,
     device_agent      VARCHAR(255) NOT NULL,
-    device_ping       TEXT,
     device_supported  TEXT,
     device_policykey  BIGINT DEFAULT 0,
     device_rwstatus   INTEGER,
-    device_folders    TEXT,
-    device_user       VARCHAR(255),
-
 --
     PRIMARY KEY (device_id)
 );
 
-CREATE INDEX activesync_device_user_idx ON horde_activesync_device (device_user);
\ No newline at end of file
+CREATE TABLE horde_activesync_device_users (
+    device_id         VARCHAR(255) NOT NULL,
+    device_user       VARCHAR(255) NOT NULL,
+    device_ping       TEXT,
+    device_folders    TEXT,
+--
+    PRIMARY KEY (device_id, device_user)
+);
+CREATE INDEX activesync_device_users_idx ON horde_activesync_device_users (device_user);
index 02854d8..d8749ea 100644 (file)
@@ -181,6 +181,7 @@ CREATE TABLE horde_activesync_state (
     sync_data          text,
     sync_devid         varchar(255),
     sync_folderid      varchar(255),
+    sync_user          varchar(255),
 --
     PRIMARY KEY (sync_key)
 );
@@ -191,7 +192,8 @@ CREATE TABLE horde_activesync_map (
     sync_modtime       numeric(10, 0),
     sync_key           varchar(255) NOT NULL,
     sync_devid         varchar(255) NOT NULL,
-    sync_folderid      varchar(255) NOT NULL
+    sync_folderid      varchar(255) NOT NULL,
+    sync_user          varchar(255)
 );
 go
 
@@ -199,18 +201,24 @@ CREATE TABLE horde_activesync_device (
     device_id         varchar(255) NOT NULL,
     device_type       varchar(255) NOT NULL,
     device_agent      varchar(255) NOT NULL,
-    device_ping       text,
     device_supported  text,
     device_policykey  number(11, 0) DEFAULT 0,
     device_rwstatus   number(10, 0),
-    device_folders    text,
-    device_user       varchar(255),
-
 --
     PRIMARY KEY (device_id)
 );
 go
 
+CREATE TABLE horde_activesync_device_users (
+    device_id         varchar(255) NOT NULL,
+    device_user       varchar(255) NOT NULL,
+    device_ping       text,
+    device_folders    text,
+--
+    PRIMARY KEY (device_id, device_user)
+);
+go
+
 -- CREATE TABLE horde_datatree_seq (
 --   id numeric(10,0) IDENTITY NOT NULL,
 --   PRIMARY KEY (id)
@@ -332,6 +340,12 @@ go
 CREATE INDEX activesync_device_user_idx ON horde_activesync_device (device_user);
 go
 
+CREATE INDEX activesync_device_users_idx ON horde_activesync_device_users (device_user);
+go
+
+CREATE INDEX activesync_map_user_idx ON horde_activesync_map (sync_user);
+go
+
 -- CREATE INDEX vfs_path_idx ON horde_vfs (vfs_path)
 -- go
 
index eb30d29..06ae2df 100644 (file)
     <notnull>false</notnull>
    </field>
 
+   <field>
+    <name>sync_user</name>
+    <type>text</type>
+    <length>255</length>
+    <notnull>false</notnull>
+   </field>
+
    <index>
     <name>activesync_state_primary</name>
     <primary>true</primary>
     <notnull>false</notnull>
    </field>
 
+   <field>
+    <name>sync_user</name>
+    <type>text</type>
+    <length>255</length>
+    <notnull>false</notnull>
+   </field>
+
    <index>
     <name>activesync_map_message_idx</name>
     <field>
     </field>
    </index>
 
+   <index>
+    <name>activesync_map_user_idx</name>
+    <field>
+     <name>sync_user</name>
+    </field>
+   </index>
+
   </declaration>
 
  </table>
    </field>
 
    <field>
-    <name>device_ping</name>
-    <type>clob</type>
-    <notnull>false</notnull>
-   </field>
-
-   <field>
     <name>device_supported</name>
     <type>clob</type>
     <notnull>false</notnull>
     <notnull>false</notnull>
    </field>
 
+   <index>
+    <name>activesync_device_primary</name>
+    <primary>true</primary>
+    <field>
+     <name>device_id</name>
+    </field>
+   </index>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>horde_activesync_device_users</name>
+
+  <declaration>
+   <field>
+    <name>device_ping</name>
+    <type>clob</type>
+    <notnull>false</notnull>
+   </field>
+
    <field>
     <name>device_folders</name>
     <type>clob</type>
    </field>
 
    <index>
-    <name>activesync_device_primary</name>
-    <primary>true</primary>
-    <field>
-     <name>device_id</name>
-    </field>
-   </index>
-
-   <index>
-    <name>activesync_device_user_message_idx</name>
+    <name>activesync_device_user_idx</name>
     <field>
      <name>device_user</name>
     </field>
   </declaration>
 
  </table>
-
 </database>
index 92546b5..ae64a7f 100644 (file)
@@ -4,6 +4,7 @@ CREATE TABLE horde_activesync_state (
     sync_data          TEXT,
     sync_devid         VARCHAR(255),
     sync_folderid      VARCHAR(255),
+    sync_user          VARCHAR(255) NOT NULL,
 --
     PRIMARY KEY (sync_key)
 );
@@ -16,26 +17,34 @@ CREATE TABLE horde_activesync_map (
     sync_modtime       INTEGER,
     sync_key           VARCHAR(255) NOT NULL,
     sync_devid         VARCHAR(255) NOT NULL,
-    sync_folderid      VARCHAR(255) NOT NULL
+    sync_folderid      VARCHAR(255) NOT NULL,
+    sync_user          VARCHAR(255) NOT NULL
 );
 
 CREATE INDEX activesync_map_devid_idx ON horde_activesync_map (sync_devid);
 CREATE INDEX activesync_map_message_idx ON horde_activesync_map (message_uid);
+CREATE INDEX activesync_map_user_idx ON horde_activesync_map (sync_user);
+
 
 CREATE TABLE horde_activesync_device (
     device_id         VARCHAR(255) NOT NULL,
     device_type       VARCHAR(255) NOT NULL,
     device_agent      VARCHAR(255) NOT NULL,
-    device_ping       TEXT,
     device_supported  TEXT,
     device_policykey  BIGINT DEFAULT 0,
     device_rwstatus   INTEGER,
-    device_folders    TEXT,
-    device_user       VARCHAR(255),
-
 --
     PRIMARY KEY (device_id)
 );
 
-CREATE INDEX activesync_device_user_idx ON horde_activesync_device (device_user);
+CREATE TABLE horde_activesync_device_users (
+    device_id         VARCHAR(255) NOT NULL,
+    device_user       VARCHAR(255) NOT NULL,
+    device_ping       TEXT,
+    device_folders    TEXT,
+--
+    PRIMARY KEY (device_id, device_user)
+);
+
+CREATE INDEX activesync_device_user_idx ON horde_activesync_users (device_user);
 
diff --git a/horde/scripts/upgrades/2010-05-11_horde_activesync_adduserkey.sql b/horde/scripts/upgrades/2010-05-11_horde_activesync_adduserkey.sql
new file mode 100644 (file)
index 0000000..88f0d9f
--- /dev/null
@@ -0,0 +1,21 @@
+ALTER TABLE horde_activesync_state ADD COLUMN sync_user VARCHAR(255);
+
+ALTER TABLE horde_activesync_device DROP COLUMN device_user;
+ALTER TABLE horde_activesync_device DROP COLUMN device_ping;
+ALTER TABLE horde_activesync_device DROP COLUMN device_folders;
+
+DROP INDEX activesync_device_user_idx;
+
+CREATE TABLE horde_activesync_device_users (
+    device_id         VARCHAR(255) NOT NULL,
+    device_user       VARCHAR(255) NOT NULL,
+    device_ping       TEXT,
+    device_folders    TEXT,
+--
+    PRIMARY KEY (device_id, device_user)
+);
+CREATE INDEX activesync_device_users_idx ON horde_activesync_device_users (device_user);
+
+ALTER TABLE horde_activesync_map ADD COLUMN sync_user VARCHAR(255);
+CREATE INDEX activesync_map_user_idx ON horde_activesync_map (sync_user);
+
index 3d5f4a4..9f11dcc 100644 (file)
@@ -9,6 +9,7 @@
 <input type="hidden" id="removedevice" name="removedevice" />
 <input type="hidden" name="wipeid" id="wipeid" />
 <input type="hidden" name="cancelwipe" id="cancelwipe" />
+<input type="hidden" name="removeuser" id="removeuser" value="<tag:username />" />
 <table class="horde-table striped">
  <tr>
      <th></th>
@@ -25,7 +26,7 @@
    <if:devices.ispending>
     <input class="button" type="button" value="<gettext>Cancel Wipe</gettext>" onclick="HordeActiveSyncPrefs.cancelRemoteWipe('<tag:devices.device_id />');" />
    </if:devices.ispending>
-   <input class="button" type="button" value="<gettext>Remove</gettext>" onclick="HordeActiveSyncPrefs.removeDevice('<tag:devices.device_id />');" />
+   <input class="button" type="button" value="<gettext>Remove</gettext>" onclick="HordeActiveSyncPrefs.removeDevice('<tag:devices.device_id />', '<tag:devices.device_user />');" />
   </td>
   <td><tag:devices.device_type /></td>
   <td><tag:devices.ts /></td>