Overview

Packages

  • ActiveSync
  • None

Classes

  • Horde_ActiveSync_Message_Appointment
  • Horde_ActiveSync_Message_Attendee
  • Horde_ActiveSync_Message_Contact
  • Horde_ActiveSync_Message_Exception
  • Horde_ActiveSync_Message_Folder
  • Horde_ActiveSync_Message_Recurrence
  • Horde_ActiveSync_Message_Task
  • Horde_ActiveSync_State_Base
  • Horde_ActiveSync_State_History
  • Horde_ActiveSync_Timezone
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Base class for managing everything related to state:
  4:  *
  5:  *     Persistence of state data
  6:  *     Generating delta between server and PIM
  7:  *     Caching PING related state (hearbeat interval, folder list etc...)
  8:  *
  9:  * Copyright 2010-2012 Horde LLC (http://www.horde.org/)
 10:  *
 11:  * @author Michael J. Rubinsky <mrubinsk@horde.org>
 12:  * @package ActiveSync
 13:  */
 14: abstract class Horde_ActiveSync_State_Base
 15: {
 16:     /**
 17:      * Filtertype constants
 18:      */
 19:     const FILTERTYPE_ALL = 0;
 20:     const FILTERTYPE_1DAY = 1;
 21:     const FILTERTYPE_3DAYS = 2;
 22:     const FILTERTYPE_1WEEK = 3;
 23:     const FILTERTYPE_2WEEKS = 4;
 24:     const FILTERTYPE_1MONTH = 5;
 25:     const FILTERTYPE_3MONTHS = 6;
 26:     const FILTERTYPE_6MONTHS = 7;
 27: 
 28:     /**
 29:      * Configuration parameters
 30:      *
 31:      * @var array
 32:      */
 33:     protected $_params;
 34: 
 35:     /**
 36:      * Caches the current state(s) in memory
 37:      *
 38:      * @var array
 39:      */
 40:     protected $_stateCache;
 41: 
 42:     /**
 43:      * The syncKey for the current request.
 44:      *
 45:      * @var string
 46:      */
 47:     protected $_syncKey;
 48: 
 49:     /**
 50:      * The backend driver
 51:      *
 52:      * @param Horde_ActiveSync_Driver_Base
 53:      */
 54:     protected $_backend;
 55: 
 56:     /**
 57:      * Cache for ping state
 58:      *
 59:      * @var array
 60:      */
 61:     protected $_pingState;
 62: 
 63:     /**
 64:      * The collection array for the collection we are currently syncing.
 65:      * Keys include:
 66:      *   'class'      - The collection class Contacts, Calendar etc...
 67:      *   'synckey'    - The current synckey
 68:      *   'newsynckey' - The new synckey sent back to the PIM
 69:      *   'id'         - Server folder id
 70:      *   'filtertype' - Filter
 71:      *   'conflict'   - Conflicts
 72:      *   'truncation' - Truncation
 73:      *
 74:      *
 75:      * @var array
 76:      */
 77:     protected $_collection;
 78: 
 79:     /**
 80:      * Logger instance
 81:      *
 82:      * @var Horde_Log_Logger
 83:      */
 84:     protected $_logger;
 85: 
 86:     /**
 87:      * The PIM device id. Needed for PING requests
 88:      *
 89:      * @var string
 90:      */
 91:     protected $_devId;
 92: 
 93:     /**
 94:      * Device info cache
 95:      *
 96:      * @var object
 97:      */
 98:     protected $_deviceInfo;
 99: 
100:     /**
101:      * Local cache for changes to *send* to PIM
102:      * (Will remain null until getChanges() is called)
103:      *
104:      * @var
105:      */
106:     protected $_changes;
107: 
108:     /**
109:      * The type of request we are handling (if important).
110:      *
111:      * @var string
112:      */
113:     protected $_type;
114: 
115:     /**
116:      * Const'r
117:      *
118:      * @param array $collection  A collection array
119:      * @param array $params  All configuration parameters, requirements.
120:      *
121:      * @return Horde_ActiveSync_State_Base
122:      */
123:     public function __construct($params = array())
124:     {
125:         $this->_params = $params;
126:         if (empty($params['logger'])) {
127:             $this->_logger = new Horde_Support_Stub();
128:         }
129:     }
130: 
131:     public function __destruct()
132:     {
133:         unset ($this->_backend);
134:     }
135: 
136:     /**
137:      * Update the $oldKey syncState to $newKey.
138:      *
139:      * @param string $newKey
140:      *
141:      * @return void
142:      */
143:     public function setNewSyncKey($newKey)
144:     {
145:         $this->_syncKey = $newKey;
146:     }
147: 
148:     /**
149:      * Get the current synckey
150:      *
151:      * @return string  The synkey we last retrieved state for
152:      */
153:     public function getCurrentSyncKey()
154:     {
155:         return $this->_syncKey;
156:     }
157: 
158:     /**
159:      * Generate a random 10 digit policy key
160:      *
161:      * @return unknown
162:      */
163:     public function generatePolicyKey()
164:     {
165:         return mt_rand(1000000000, 9999999999);
166:     }
167: 
168:     /**
169:      * Obtain the current policy key, if it exists.
170:      *
171:      * @param string $devId     The device id to obtain policy key for.
172:      *
173:      * @return integer  The current policy key for this device, or 0 if none
174:      *                  exists.
175:      */
176:     public function getPolicyKey($devId)
177:     {
178:         //@TODO - combine _devId and _deviceInfo
179:         /* See if we have it already */
180:         if (empty($this->_deviceInfo) || $this->_devId != $devId) {
181:             throw new Horde_ActiveSync_Exception('Device not loaded.');
182:         }
183: 
184:         return $this->_deviceInfo->policykey;
185:     }
186: 
187:     /**
188:      * Return a device wipe status
189:      *
190:      * @param string $devId
191:      *
192:      * @return integer
193:      */
194:     public function getDeviceRWStatus($devId)
195:     {
196:         //@TODO - combine _devId and _deviceInfo
197:         /* See if we have it already */
198:         if (empty($this->_deviceInfo) || $this->_devId != $devId) {
199:             throw new Horde_ActiveSync_Exception('Device not loaded.');
200:         }
201: 
202:         return $this->_deviceInfo->rwstatus;
203:     }
204: 
205:     /**
206:      * Set the backend driver
207:      * (should really only be called by a backend object when passing this
208:      * object to client code)
209:      *
210:      * @param Horde_ActiveSync_Driver_Base $backend  The backend driver
211:      *
212:      * @return void
213:      */
214:     public function setBackend(Horde_ActiveSync_Driver_Base $backend)
215:     {
216:         $this->_backend = $backend;
217:     }
218: 
219:     /**
220:      * Initialize the state object
221:      *
222:      * @param array $collection  The collection array
223:      *
224:      * @return void
225:      */
226:     public function init($collection = array())
227:     {
228:         $this->_collection = $collection;
229:     }
230: 
231:     /**
232:      * Set the logger instance for this object.
233:      *
234:      * @param Horde_Log_Logger $logger
235:      */
236:     public function setLogger($logger)
237:     {
238:         $this->_logger = $logger;
239:     }
240: 
241:     /**
242:      * Reset the device's PING state.
243:      *
244:      * @return void
245:      */
246:     public function resetPingState()
247:     {
248:         $this->_logger->debug('Resetting PING state');
249:         $this->_pingState = array(
250:             'lifetime' => 0,
251:             'collections' => array());
252:     }
253: 
254:     /**
255:      * Get the number of server changes.
256:      *
257:      * @return integer
258:      */
259:     public function getChangeCount()
260:     {
261:         if (!isset($this->_changes)) {
262:             $this->getChanges();
263:         }
264: 
265:         return count($this->_changes);
266:     }
267: 
268:     /**
269:      * Gets the new sync key for a specified sync key. You must save the new
270:      * sync state under this sync key when done sync'ing by calling
271:      * setNewSyncKey(), then save().
272:      *
273:      * @param string $syncKey  The old syncKey
274:      *
275:      * @return string  The new synckey
276:      * @throws Horde_ActiveSync_Exception
277:      */
278:     static public function getNewSyncKey($syncKey)
279:     {
280:         if (empty($syncKey)) {
281:             return '{' . new Horde_Support_Uuid() . '}' . '1';
282:         } else {
283:             if (preg_match('/^s{0,1}\{([a-fA-F0-9-]+)\}([0-9]+)$/', $syncKey, $matches)) {
284:                 $n = $matches[2];
285:                 $n++;
286: 
287:                 return '{' . $matches[1] . '}' . $n;
288:             }
289:             throw new Horde_ActiveSync_Exception('Invalid SyncKey format passed to getNewSyncKey()');
290:         }
291:     }
292: 
293:     /**
294:      * Return the counter for the specified syncKey.
295:      *
296:      * @return mixed integer|boolean
297:      */
298:     static public function getSyncKeyCounter($syncKey)
299:     {
300:        if (preg_match('/^s{0,1}\{([a-fA-F0-9-]+)\}([0-9]+)$/', $syncKey, $matches)) {
301:             $n = $matches[2];
302:             return $n;
303:         }
304: 
305:         return false;
306:     }
307: 
308:    /**
309:     * Returns the timestamp of the earliest modification time to consider
310:     *
311:     * @param integer $restrict  The time period to restrict to
312:     *
313:     * @return integer
314:     */
315:     static protected function _getCutOffDate($restrict)
316:     {
317:         switch($restrict) {
318:         case self::FILTERTYPE_1DAY:
319:             $back = 60 * 60 * 24;
320:             break;
321:         case self::FILTERTYPE_3DAYS:
322:             $back = 60 * 60 * 24 * 3;
323:             break;
324:         case self::FILTERTYPE_1WEEK:
325:             $back = 60 * 60 * 24 * 7;
326:             break;
327:         case self::FILTERTYPE_2WEEKS:
328:             $back = 60 * 60 * 24 * 14;
329:             break;
330:         case self::FILTERTYPE_1MONTH:
331:             $back = 60 * 60 * 24 * 31;
332:             break;
333:         case self::FILTERTYPE_3MONTHS:
334:             $back = 60 * 60 * 24 * 31 * 3;
335:             break;
336:         case self::FILTERTYPE_6MONTHS:
337:             $back = 60 * 60 * 24 * 31 * 6;
338:             break;
339:         default:
340:             break;
341:         }
342: 
343:         if (isset($back))
344:         {
345:             $date = time() - $back;
346:             return $date;
347:         } else {
348:             return 0; // unlimited
349:         }
350:     }
351: 
352:     /**
353:      * Helper function that performs the actual diff between PIM state and
354:      * server state arrays.
355:      *
356:      * @param array $old  The PIM state
357:      * @param array $new  The current server state
358:      *
359:      * @return unknown_type
360:      */
361:     protected function _getDiff($old, $new)
362:     {
363:         $changes = array();
364: 
365:         // Sort both arrays in the same way by ID
366:         usort($old, array(__CLASS__, 'RowCmp'));
367:         usort($new, array(__CLASS__, 'RowCmp'));
368: 
369:         $inew = 0;
370:         $iold = 0;
371: 
372:         // Get changes by comparing our list of messages with
373:         // our previous state
374:         while (1) {
375:             $change = array();
376: 
377:             if ($iold >= count($old) || $inew >= count($new)) {
378:                 break;
379:             }
380: 
381:             if ($old[$iold]['id'] == $new[$inew]['id']) {
382:                 // Both messages are still available, compare flags and mod
383:                 if (isset($old[$iold]['flags']) && isset($new[$inew]['flags']) && $old[$iold]['flags'] != $new[$inew]['flags']) {
384:                     // Flags changed
385:                     $change['type'] = 'flags';
386:                     $change['id'] = $new[$inew]['id'];
387:                     $change['flags'] = $new[$inew]['flags'];
388:                     $changes[] = $change;
389:                 }
390: 
391:                 if ($old[$iold]['mod'] != $new[$inew]['mod']) {
392:                     $change['type'] = 'change';
393:                     $change['id'] = $new[$inew]['id'];
394:                     $changes[] = $change;
395:                 }
396: 
397:                 $inew++;
398:                 $iold++;
399:             } else {
400:                 if ($old[$iold]['id'] > $new[$inew]['id']) {
401:                     // Message in state seems to have disappeared (delete)
402:                     $change['type'] = 'delete';
403:                     $change['id'] = $old[$iold]['id'];
404:                     $changes[] = $change;
405:                     $iold++;
406:                 } else {
407:                     // Message in new seems to be new (add)
408:                     $change['type'] = 'change';
409:                     $change['flags'] = Horde_ActiveSync::FLAG_NEWMESSAGE;
410:                     $change['id'] = $new[$inew]['id'];
411:                     $changes[] = $change;
412:                     $inew++;
413:                 }
414:             }
415:         }
416: 
417:         while ($iold < count($old)) {
418:             // All data left in _syncstate have been deleted
419:             $change['type'] = 'delete';
420:             $change['id'] = $old[$iold]['id'];
421:             $changes[] = $change;
422:             $iold++;
423:         }
424: 
425:         while ($inew < count($new)) {
426:             // All data left in new have been added
427:             $change['type'] = 'change';
428:             $change['flags'] = Horde_ActiveSync::FLAG_NEWMESSAGE;
429:             $change['id'] = $new[$inew]['id'];
430:             $changes[] = $change;
431:             $inew++;
432:         }
433: 
434:         return $changes;
435:     }
436: 
437:     /**
438:      * Helper function for the _diff method
439:      *
440:      * @param $a
441:      * @param $b
442:      * @return unknown_type
443:      */
444:     static public function RowCmp($a, $b)
445:     {
446:         return $a['id'] < $b['id'] ? 1 : -1;
447:     }
448: 
449:     /**
450:      * Loads the initial state from storage for the specified syncKey and
451:      * intializes the stateMachine for use.
452:      *
453:      * @param string $syncKey  The key for the state to load.
454:      * @param string $type     Treat the loaded state data as this type of state.
455:      * @param string $id       The collection id this represents
456:      *
457:      * @return array The state array
458:      */
459:     abstract public function loadState($syncKey, $type = null, $id = '');
460: 
461:     /**
462:      * Load/initialize the ping state for the specified device.
463:      *
464:      * @param object $device
465:      */
466:     abstract public function initPingState($device);
467: 
468:     /**
469:      * Load the ping state for the given device id
470:      *
471:      * @param string $devid  The device id.
472:      */
473:     abstract public function loadPingCollectionState($devid);
474: 
475:     /**
476:      * Get the list of known folders for the specified syncState
477:      *
478:      * @return array  An array of server folder ids
479:      */
480:     abstract public function getKnownFolders();
481: 
482:     /**
483:      * Save the current syncstate to storage
484:      */
485:     abstract public function save();
486: 
487:     /**
488:      * Update the state to reflect changes
489:      *
490:      * @param string $type     The type of change (change, delete, flags)
491:      * @param array $change    A stat/change hash describing the change
492:      * @param integer $origin  Flag to indicate the origin of the change.
493:      * @param string $user     The current synch user
494:      *
495:      * @return void
496:      */
497:     abstract public function updateState($type, array $change,
498:                                          $origin = Horde_ActiveSync::CHANGE_ORIGIN_NA,
499:                                          $user = null);
500: 
501:     /**
502:      * Save folder data for a specific device. This is needed for BC with older
503:      * activesync versions that use GETHIERARCHY requests to get the folder info
504:      * instead of maintaining the folder state with FOLDERSYNC requests.
505:      *
506:      * @param object $device  The device object
507:      * @param array $folders  The folder data
508:      *
509:      * @return boolean
510:      * @throws Horde_ActiveSync_Exception
511:      */
512:     abstract public function setFolderData($device, $folders);
513: 
514:     /**
515:      * Get the folder data for a specific device
516:      *
517:      * @param object $device  The device object
518:      * @param string $class   The folder class to fetch (Calendar, Contacts etc.)
519:      *
520:      * @return mixed  Either an array of folder data || false
521:      */
522:     abstract public function getFolderData($device, $class);
523: 
524:     /**
525:      * Get all items that have changed since the last sync time
526:      *
527:      * @param integer $flags
528:      *
529:      * @return array
530:      */
531:     abstract public function getChanges($flags = 0);
532: 
533:     /**
534:      * Determines if the server version of the message represented by $stat
535:      * conflicts with the PIM version of the message according to the current
536:      * state.
537:      *
538:      * @param array $stat   A message stat array
539:      * @param string $type  The type of change (change, delete, add)
540:      *
541:      * @return boolean
542:      */
543:     abstract public function isConflict($stat, $type);
544: 
545:     /**
546:      * Save a new device policy key to storage.
547:      *
548:      * @param string $devId  The device id
549:      * @param integer $key   The new policy key
550:      */
551:     abstract public function setPolicyKey($devId, $key);
552: 
553:     /**
554:      * Reset ALL device policy keys. Used when server policies have changed
555:      * and you want to force ALL devices to pick up the changes. This will
556:      * cause all devices that support provisioning to be reprovisioned.
557:      *
558:      * @throws Horde_ActiveSync_Exception
559:      *
560:      */
561:     abstract public function resetAllPolicyKeys();
562: 
563:     /**
564:      * Set a new remotewipe status for the device
565:      *
566:      * @param string $devid
567:      * @param string $status
568:      *
569:      * @return boolean
570:      */
571:     abstract public function setDeviceRWStatus($devid, $status);
572: 
573:     /**
574:      * Obtain the device object.
575:      *
576:      * @param object $device
577:      * @param string $user
578:      *
579:      * @return StdClass
580:      */
581:     abstract public function loadDeviceInfo($device, $user);
582: 
583:     /**
584:      * Check that a given device id is known to the server. This is regardless
585:      * of Provisioning status.
586:      *
587:      * @param string $devId  The device id to check
588:      * @param string $user   The device should be owned by this user.
589:      *
590:      * @return boolean
591:      */
592:     abstract public function deviceExists($devId, $user = null);
593: 
594:     /**
595:      * Set new device info
596:      *
597:      * @param object $device  The device information
598:      *
599:      * @return boolean
600:      */
601:     abstract public function setDeviceInfo($data);
602: 
603:     /**
604:      * Explicitly remove a state from storage.
605:      *
606:      * @param string $synckey  The specific state to remove
607:      * @param string $devId    Remove all state for this device (ignores synckey)
608:      *
609:      * @throws Horde_ActiveSyncException
610:      */
611:     abstract public function removeState($synckey = null, $devId = null);
612: 
613:     /**
614:      * Return the heartbeat interval, or zero if we have no existing state
615:      *
616:      * @return integer  The hearbeat interval, or zero if not found.
617:      * @throws Horde_ActiveSync_Exception
618:      */
619:     abstract public function getHeartbeatInterval();
620: 
621:     /**
622:      * Set the device's heartbeat interval
623:      *
624:      * @param integer $heartbeat  The interval (in seconds).
625:      */
626:     abstract public function setHeartbeatInterval($heartbeat);
627: 
628:     /**
629:      * List all devices that we know about.
630:      *
631:      * @return array  An array of device hashes
632:      * @throws Horde_ActiveSync_Exception
633:      */
634:     abstract public function listDevices();
635: 
636:     /**
637:      * Get the last time the currently loaded device issued a SYNC request.
638:      *
639:      * @return integer  The timestamp of the last sync, regardless of collection
640:      * @throws Horde_ActiveSync_Exception
641:      */
642:     abstract public function getLastSyncTimestamp();
643: 
644: }
API documentation generated by ApiGen