Overview

Packages

  • ActiveSync
  • None

Classes

  • Horde_ActiveSync
  • Horde_ActiveSync_Connector_Exporter
  • Horde_ActiveSync_Connector_Importer
  • Horde_ActiveSync_Driver_Base
  • Horde_ActiveSync_Exception
  • Horde_ActiveSync_Exception_InvalidRequest
  • Horde_ActiveSync_Exception_StateGone
  • Horde_ActiveSync_Message_Base
  • Horde_ActiveSync_Request_Base
  • Horde_ActiveSync_Request_FolderCreate
  • Horde_ActiveSync_Request_FolderSync
  • Horde_ActiveSync_Request_GetHierarchy
  • Horde_ActiveSync_Request_GetItemEstimate
  • Horde_ActiveSync_Request_MeetingResponse
  • Horde_ActiveSync_Request_MoveItems
  • Horde_ActiveSync_Request_Notify
  • Horde_ActiveSync_Request_Ping
  • Horde_ActiveSync_Request_Provision
  • Horde_ActiveSync_Request_Search
  • Horde_ActiveSync_Request_SendMail
  • Horde_ActiveSync_Request_SmartForward
  • Horde_ActiveSync_Request_SmartReply
  • Horde_ActiveSync_Request_Sync
  • Horde_ActiveSync_State_File
  • Horde_ActiveSync_Sync
  • Horde_ActiveSync_Wbxml
  • Horde_ActiveSync_Wbxml_Decoder
  • Horde_ActiveSync_Wbxml_Encoder
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Base class for ActiveSync backends. Provides the communication between
  4:  * the ActiveSync classes and the actual backend data that is being sync'd.
  5:  *
  6:  * Also responsible for providing objects to the command objects that can
  7:  * generate the delta between the PIM and server.
  8:  *
  9:  * Based, in part, on code by the Z-Push project. Original copyright notices
 10:  * appear below.
 11:  *
 12:  * Copyright 2010-2012 Horde LLC (http://www.horde.org/)
 13:  *
 14:  * @author Michael J. Rubinsky <mrubinsk@horde.org>
 15:  * @package ActiveSync
 16:  */
 17: /**
 18:  * File      :   diffbackend.php
 19:  * Project   :   Z-Push
 20:  * Descr     :   We do a standard differential
 21:  *               change detection by sorting both
 22:  *               lists of items by their unique id,
 23:  *               and then traversing both arrays
 24:  *               of items at once. Changes can be
 25:  *               detected by comparing items at
 26:  *               the same position in both arrays.
 27:  *
 28:  * Created   :   01.10.2007
 29:  *
 30:  * Zarafa Deutschland GmbH, www.zarafaserver.de
 31:  * This file is distributed under GPL-2.0.
 32:  * Consult COPYING file for details
 33:  */
 34: abstract class Horde_ActiveSync_Driver_Base
 35: {
 36:     /**
 37:      * The username to sync with the backend as
 38:      *
 39:      * @var string
 40:      */
 41:     protected $_user;
 42: 
 43:     /**
 44:      * Authenticating user
 45:      *
 46:      * @var string
 47:      */
 48:     protected $_authUser;
 49: 
 50:     /**
 51:      * User password
 52:      *
 53:      * @var string
 54:      */
 55:     protected $_authPass;
 56: 
 57:     /**
 58:      * Logger instance
 59:      *
 60:      * @var Horde_Log_Logger
 61:      */
 62:     protected $_logger;
 63: 
 64:     /**
 65:      * Parameters
 66:      *
 67:      * @var array
 68:      */
 69:     protected $_params;
 70: 
 71:     /**
 72:      * Secuirity Policies. These settings can be overridden by the backend
 73:      * provider by passing in a 'policies' key in the const'r params array. This
 74:      * way the server can provide user-specific policies.
 75:      *
 76:      * <pre>
 77:      * Currently supported settings are:
 78:      *   pin      - Device must have a pin lock enabled.
 79:      *   computerunlock  - Device can be unlocked by a computer.
 80:      *   AEFrequencyValue - Time (in minutes) of inactivity before device locks
 81:      *   DeviceWipeThreshold - Number of failed unlock attempts before the
 82:      *                         device should wipe on devices that support this.
 83:      *   CodewordFrequency   - Number of failed unlock attempts before needing
 84:      *                         to verify that a person who can read and write is
 85:      *                         using the PIM.
 86:      *   MinimumPasswordLength
 87:      *   PasswordComplexity     - 0 - alphanumeric, 1 - numeric, 2 - anything
 88:      * </pre>
 89:      */
 90:     protected $_policies = array(
 91:         'pin' => true,
 92:         'extended_policies' => true,
 93:         'inactivity' => 5,
 94:         'wipethreshold' => 10,
 95:         'codewordfrequency' => 0,
 96:         'minimumlength' => 5,
 97:         'complexity' => 2,
 98:         );
 99: 
100:     /**
101:      * The state object for this request. Needs to be injected into this class.
102:      * Different Sync objects may require more then one type of stateObject.
103:      * For instance, Horde can sync contacts and caledar data with a history
104:      * based state engine, but cannot due the same for email.
105:      *
106:      * @var Horde_ActiveSync_State_Base
107:      */
108:     protected $_stateObject;
109: 
110:     /**
111:      * Const'r
112:      *
113:      * @param array $params  Any configuration parameters or injected objects
114:      *                       the concrete driver may need.
115:      *  <pre>
116:      *     (optional) logger       Horde_Log_Logger instance
117:      *     (required) state_basic  A Horde_ActiveSync_State_Base object that is
118:      *                             capable of handling all collections except
119:      *                             email.
120:      *     (optional) state_email  A Horde_ActiveSync_State_Base object that is
121:      *                             capable of handling email collections.
122:      *  </pre>
123:      *
124:      * @return Horde_ActiveSync_Driver
125:      */
126:     public function __construct($params = array())
127:     {
128:         $this->_params = $params;
129:         if (empty($params['state_basic']) ||
130:             !($params['state_basic'] instanceof Horde_ActiveSync_State_Base)) {
131: 
132:             throw new InvalidArgumentException('Missing required state object');
133:         }
134: 
135:         /* Create a stub if we don't have a useable logger. */
136:         if (isset($params['logger'])
137:             && is_callable(array($params['logger'], 'log'))) {
138:             $this->_logger = $params['logger'];
139:             unset($params['logger']);
140:         } else {
141:             $this->_logger = new Horde_Support_Stub;
142:         }
143: 
144:         $this->_stateObject = $params['state_basic'];
145:         $this->_stateObject->setLogger($this->_logger);
146:         $this->_stateObject->setBackend($this);
147: 
148:         /* Override any security policies */
149:         if (!empty($params['policies'])) {
150:             $this->_policies = array_merge($this->_policies, $params['policies']);
151:         }
152:     }
153: 
154:     public function __destruct()
155:     {
156:         unset($this->_stateObject);
157:     }
158: 
159:     /**
160:      * Setter for the logger instance
161:      *
162:      * @param Horde_Log_Logger $logger  The logger
163:      *
164:      * @void
165:      */
166:     public function setLogger(Horde_Log_Logger $logger)
167:     {
168:         $this->_logger = $logger;
169:     }
170: 
171:     /**
172:      * Obtain the ping heartbeat settings
173:      *
174:      * @return array
175:      */
176:     public function getHeartbeatConfig()
177:     {
178:         return $this->_params['ping'];
179:     }
180: 
181:     /**
182:      * Get folder stat
183:      *  "id" => The server ID that will be used to identify the folder.
184:      *          It must be unique, and not too long. How long exactly is not
185:      *          known, but try keeping it under 20 chars or so.
186:      *          It must be a string.
187:      *  "parent" => The server ID of the parent of the folder. Same restrictions
188:      *              as 'id' apply.
189:      *  "mod" => This is the modification signature. It is any arbitrary string
190:      *           which is constant as long as the folder has not changed. In
191:      *           practice this means that 'mod' can be equal to the folder name
192:      *           as this is the only thing that ever changes in folders.
193:      */
194:     abstract public function statFolder($id);
195: 
196:     /**
197:      * Get a folder from the backend
198:      *
199:      * To be implemented by concrete backend driver.
200:      */
201:     abstract public function getFolder($id);
202: 
203:     /**
204:      * Get the list of folders from the backend.
205:      */
206:     abstract public function getFolderList();
207: 
208:     /**
209:      * Get a full list of messages on the server
210:      *
211:      * @param string $folderId       The folder id
212:      * @param timestamp $cutOffDate  The timestamp of the earliest date for
213:      *                               calendar or mail entries
214:      *
215:      * @return array  A list of messages
216:      */
217:     abstract public function getMessageList($folderId, $cutOffDate);
218: 
219:     /**
220:      * Get a list of server changes that occured during the specified time
221:      * period.
222:      *
223:      * @param string $folderId    The server id of the collection to check.
224:      * @param timestamp $from_ts  The starting timestamp
225:      * @param timestamp $to_ts    The ending timestamp
226:      *
227:      * @return array A list of messge uids that have chnaged in the specified
228:      *               time period.
229:      */
230:     abstract public function getServerChanges($folderId, $from_ts, $to_ts, $cutoffdate);
231: 
232:     /**
233:      * Get a message stat.
234:      *
235:      * @param string $folderId  The folder id
236:      * @param string $id        The message id (??)
237:      *
238:      * @return hash with 'id', 'mod', and 'flags' members
239:      */
240:     abstract public function statMessage($folderId, $id);
241: 
242:     /**
243:      * Obtain an ActiveSync message from the backend.
244:      *
245:      * @param string $folderid      The server's folder id this message is from
246:      * @param string $id            The server's message id
247:      * @param integer $truncsize    A TRUNCATION_* constant
248:      * @param integer $mimesupport  Mime support for this message
249:      *
250:      * @return Horde_ActiveSync_Message_Base The message data
251:      */
252:     abstract public function getMessage($folderid, $id, $truncsize, $mimesupport = 0);
253: 
254:     /**
255:      * Delete a message
256:      *
257:      * @param string $folderId  Folder id
258:      * @param string $id        Message id
259:      *
260:      * @return boolean
261:      */
262:     abstract public function deleteMessage($folderid, $id);
263: 
264:     /**
265:      * Add/Edit a message
266:      *
267:      * @param string $folderid  The server id for the folder the message belongs
268:      *                          to.
269:      * @param string $id        The server's uid for the message if this is a
270:      *                          change to an existing message.
271:      * @param Horde_ActiveSync_Message_Base $message  The activesync message
272:      * @param stdClass $device  The device information
273:      */
274:     abstract public function changeMessage($folderid, $id, $message, $device);
275: 
276:     /**
277:      * Any code needed to authenticate to backend as the actual user.
278:      *
279:      * @param string $username  The username to authenticate as
280:      * @param string $password  The password
281:      * @param string $domain    The user domain
282:      *
283:      * @return boolean
284:      */
285:     public function logon($username, $password, $domain = null)
286:     {
287:         $this->_authUser = $username;
288:         $this->_authPass = $password;
289: 
290:         return true;
291:     }
292: 
293:     /**
294:      * Get the username for this request.
295:      *
296:      * @return string  The current username
297:      */
298:     public function getUser()
299:     {
300:         return $this->_authUser;
301:     }
302: 
303:     /**
304:      * Any code to run on log off
305:      *
306:      * @return boolean
307:      */
308:     public function logOff()
309:     {
310:         return true;
311:     }
312: 
313:     /**
314:      * Setup sync parameters. The user provided here is the user the backend
315:      * will sync with. This allows you to authenticate as one user, and sync as
316:      * another, if the backend supports this.
317:      *
318:      * @param string $user The username to sync as on the backend.
319:      *
320:      * @return boolean
321:      */
322:     public function setup($user)
323:     {
324:         $this->_user = $user;
325: 
326:         return true;
327:     }
328: 
329:     /**
330:      * Return the helper for importing hierarchy changes from the PIM.
331:      *
332:      * @return Horde_ActiveSync_DiffState_ImportHierarchy
333:      */
334:     public function getHierarchyImporter()
335:     {
336:         $importer = new Horde_ActiveSync_DiffState_ImportHierarchy($this);
337:         $importer->setLogger($this->_logger);
338: 
339:         return $importer;
340:     }
341: 
342:     /**
343:      * Return the helper for importing message changes from the PIM.
344:      *
345:      * @param string $folderid
346:      *
347:      * @return Horde_ActiveSync_DiffState_ImportContents
348:      */
349:     public function getContentsImporter($folderId)
350:     {
351:         $importer = new Horde_ActiveSync_DiffState_ImportContents($this, $folderId);
352:         $importer->setLogger($this->_logger);
353: 
354:         return $importer;
355:     }
356: 
357:     /**
358:      * @TODO: This will replace the above two methods. (They are still called
359:      * from the (unused/unsupported MoveItems and CreateFolder Requests).
360:      *
361:      * @return Horde_ActiveSync_Connector_Importer
362:      */
363:     public function getImporter()
364:     {
365:         $importer = new Horde_ActiveSync_Connector_Importer($this);
366:         return $importer;
367:     }
368: 
369:     /**
370:      * Return helper for performing the actual sync operation.
371:      *
372:      * @param string $folderId
373:      *
374:      * @return Horde_ActiveSync_Sync
375:      */
376:     public function getSyncObject()
377:     {
378:         $exporter = new Horde_ActiveSync_Sync($this);
379:         $exporter->setLogger($this->_logger);
380: 
381:         return $exporter;
382:     }
383: 
384:     /**
385:      * Will (eventually) return an appropriate state object based on the class
386:      * being sync'd.
387:      *
388:      * @param array $collection
389:      */
390:     public function &getStateObject($collection = array())
391:     {
392:         $this->_stateObject->init($collection);
393:         $this->_stateObject->setLogger($this->_logger);
394:         return $this->_stateObject;
395:     }
396: 
397:     /**
398:      * Get the full folder hierarchy from the backend.
399:      *
400:      * @return array
401:      */
402:     public function getHierarchy()
403:     {
404:         $folders = array();
405: 
406:         $fl = $this->getFolderList();
407:         foreach ($fl as $f) {
408:             $folders[] = $this->getFolder($f['id']);
409:         }
410: 
411:         return $folders;
412:     }
413: 
414:     /**
415:      * Obtain a message from the backend.
416:      *
417:      * @param string $folderid
418:      * @param string $id
419:      * @param ?? $mimesupport  (Not sure what this was supposed to do)
420:      *
421:      * @return Horde_ActiveSync_Message_Base The message data
422:      */
423:     public function fetch($folderid, $id, $mimesupport = 0)
424:     {
425:         // Forces entire message (up to 1Mb)
426:         return $this->getMessage($folderid, $id, 1024 * 1024, $mimesupport);
427:     }
428: 
429:     /**
430:      *
431:      * @param $attname
432:      * @return unknown_type
433:      */
434:     public function getAttachmentData($attname)
435:     {
436:         return false;
437:     }
438: 
439:     /**
440:      * Sends the email represented by the rfc822 string received by the PIM.
441:      *
442:      * @param string $rfc822    The rfc822 mime message
443:      * @param boolean $forward  Is this a message forward?
444:      * @param boolean $reply    Is this a reply?
445:      * @param boolean $parent   Parent message in thread.
446:      *
447:      * @return boolean
448:      */
449:     abstract function sendMail($rfc822, $forward = false, $reply = false, $parent = false);
450: 
451:     /**
452:      * @return unknown_type
453:      */
454:     public function getWasteBasket()
455:     {
456:         return false;
457:     }
458: 
459:     /**
460:      * Delete a folder on the server.
461:      *
462:      * @param string $parent  The parent folder.
463:      * @param string $id      The folder to delete.
464:      *
465:      * @return boolean
466:      * @throws Horde_ActiveSync_Exception
467:      */
468:     public function deleteFolder($parent, $id)
469:     {
470:         throw new Horde_ActiveSync_Exception('DeleteFolder not yet implemented');
471:     }
472: 
473:     /**
474:      *
475:      * @param $folderid
476:      * @param $id
477:      * @param $flags
478:      * @return unknown_type
479:      */
480:     public function setReadFlag($folderid, $id, $flags)
481:     {
482:         return false;
483:     }
484: 
485:     /**
486:      * Change the name and/or type of a folder.
487:      *
488:      * @param string $parent
489:      * @param string $id
490:      * @param string $displayname
491:      * @param string $type
492:      *
493:      * @return boolean
494:      */
495:     public function changeFolder($parent, $id, $displayname, $type)
496:     {
497:         throw new Horde_ActiveSync_Exception('changeFolder not yet implemented.');
498:     }
499: 
500:     /**
501:      * @todo
502:      *
503:      * @param $folderid
504:      * @param $id
505:      * @param $newfolderid
506:      * @return unknown_type
507:      */
508:     public function moveMessage($folderid, $id, $newfolderid)
509:     {
510:         throw new Horde_ActiveSync_Exception('moveMessage not yet implemented.');
511:     }
512: 
513:     /**
514:      * @todo
515:      *
516:      * @param $requestid
517:      * @param $folderid
518:      * @param $error
519:      * @param $calendarid
520:      * @return unknown_type
521:      */
522:     public function meetingResponse($requestid, $folderid, $error, &$calendarid)
523:     {
524:         throw new Horde_ActiveSync_Exception('meetingResponse not yet implemented.');
525:     }
526: 
527:     /**
528:      * Returns array of items which contain contact information
529:      *
530:      * @param string $query
531:      * @param string $range
532:      *
533:      * @return array
534:      */
535:     public function getSearchResults($query, $range)
536:     {
537:         throw new Horde_ActiveSync_Exception('getSearchResults not implemented.');
538:     }
539: 
540:     /**
541:      * Specifies if this driver has an alternate way of checking for changes
542:      * when PING is used.
543:      *
544:      * @return boolean
545:      */
546:     public function alterPing()
547:     {
548:         return false;
549:     }
550: 
551:     /**
552:      * If this driver can check for changes in an alternate way for PING then
553:      * for SYNC, this method is used to do so. Also, alterPing() should return
554:      * true in this case.
555:      *
556:      * @param string $folderid  The folder id
557:      * @param array $syncstate  The syncstate
558:      *
559:      * @deprecated - This will probably be removed.
560:      * @return array  An array of changes, the same as retunred from getChanges
561:      */
562:     public function alterPingChanges($folderid, &$syncstate)
563:     {
564:         return array();
565:     }
566: 
567:     /**
568:      * Build a <wap-provisioningdoc> for the given security settings provided
569:      * by the backend.
570:      *
571:      * 4131 (Enforce password on device) 0: enabled 1: disabled
572:      * AEFrequencyType 0: no inactivity time 1: inactivity time is set
573:      * AEFrequencyValue inactivity time in minutes
574:      * DeviceWipeThreshold after how many wrong password to device should get wiped
575:      * CodewordFrequency validate every x wrong passwords, that a person is using the device which is able to read and write. should be half of DeviceWipeThreshold
576:      * MinimumPasswordLength minimum password length
577:      * PasswordComplexity 0: Require alphanumeric 1: Require only numeric, 2: anything goes
578:      *
579:      * @param string  The type of policy to return.
580:      *
581:      * @return string
582:      */
583:     public function getCurrentPolicy($policyType = 'MS-WAP-Provisioning-XML')
584:     {
585:         $xml = '<wap-provisioningdoc><characteristic type="SecurityPolicy">'
586:             . '<parm name="4131" value="' . ($this->_policies['pin'] ? 0 : 1) . '"/>'
587:             . '</characteristic>';
588: 
589:         if ($this->_policies['pin'] && $this->_policies['extended_policies']) {
590:             $xml .= '<characteristic type="Registry">'
591:             .   '<characteristic type="HKLM\Comm\Security\Policy\LASSD\AE\{50C13377-C66D-400C-889E-C316FC4AB374}">'
592:             .        '<parm name="AEFrequencyType" value="' . (!empty($this->_policies['inactivity']) ? 1 : 0) . '"/>'
593:             .        (!empty($this->_policies['inactivity']) ? '<parm name="AEFrequencyValue" value="' . $this->_policies['inactivity'] . '"/>' : '')
594:             .    '</characteristic>';
595: 
596:             if (!empty($this->_policies['wipethreshold'])) {
597:                 $xml .= '<characteristic type="HKLM\Comm\Security\Policy\LASSD"><parm name="DeviceWipeThreshold" value="' . $this->_policies['wipethreshold'] . '"/></characteristic>';
598:             }
599:             if (!empty($this->_policies['codewordfrequency'])) {
600:                 $xml .= '<characteristic type="HKLM\Comm\Security\Policy\LASSD"><parm name="CodewordFrequency" value="' . $this->_policies['codewordfrequency'] . '"/></characteristic>';
601:             }
602:             if (!empty($this->_policies['minimumlength'])) {
603:                 $xml .= '<characteristic type="HKLM\Comm\Security\Policy\LASSD\LAP\lap_pw"><parm name="MinimumPasswordLength" value="' . $this->_policies['minimumlength'] . '"/></characteristic>';
604:             }
605:             if ($this->_policies['complexity'] !== false) {
606:                 $xml .= '<characteristic type="HKLM\Comm\Security\Policy\LASSD\LAP\lap_pw"><parm name="PasswordComplexity" value="' . $this->_policies['complexity'] . '"/></characteristic>';
607:             }
608:             $xml .= '</characteristic>';
609:         }
610: 
611:         $xml .= '</wap-provisioningdoc>';
612: 
613:         return $xml;
614:     }
615: 
616:     /**
617:      * Truncate an UTF-8 encoded sting correctly
618:      *
619:      * If it's not possible to truncate properly, an empty string is returned
620:      *
621:      * @param string $string  The string to truncate
622:      * @param string $length  The length of the returned string
623:      *
624:      * @return string  The truncated string
625:      */
626:     static public function truncate($string, $length)
627:     {
628:         if (strlen($string) <= $length) {
629:             return $string;
630:         }
631:         while($length >= 0) {
632:             if ((ord($string[$length]) < 0x80) || (ord($string[$length]) >= 0xC0)) {
633:                 return substr($string, 0, $length);
634:             }
635:             $length--;
636:         }
637: 
638:         return "";
639:     }
640: 
641: }
642: 
API documentation generated by ApiGen