1<?php
2/**
3 * @license   http://www.horde.org/licenses/gpl GPLv2
4 *
5 * @copyright 2009-2020 Horde LLC (http://www.horde.org)
6 * @author    Michael J Rubinsky <mrubinsk@horde.org>
7 * @package   ActiveSync
8 */
9
10/**
11 * The Horde ActiveSync server. Entry point for performing all ActiveSync
12 * operations.
13 *
14 * @license   http://www.horde.org/licenses/gpl GPLv2
15 *
16 * @copyright 2009-2020 Horde LLC (http://www.horde.org)
17 * @author    Michael J Rubinsky <mrubinsk@horde.org>
18 * @package   ActiveSync
19 *
20 * @property-read Horde_ActiveSync_Wbxml_Encoder $encoder The Wbxml encoder.
21 * @property-read Horde_ActiveSync_Wbxml_Decoder $decoder The Wbxml decoder.
22 * @property-read Horde_ActiveSync_State_Base $state      The state object.
23 * @property-read Horde_Controller_Reqeust_Http $request  The HTTP request object.
24 * @property-read Horde_ActiveSync_Driver_Base $driver    The backend driver object.
25 * @property-read boolean|string $provisioning Provisioning support: True, False, or 'loose'
26 * @property-read boolean $multipart Indicate this is a multipart request.
27 * @property-read string $certPath Local path to the certificate bundle.
28 * @property-read Horde_ActiveSync_Device $device  The current device object.
29 * @property-read Horde_ActiveSync_Log_Logger $logger   The logger object.
30 */
31class Horde_ActiveSync
32{
33    /* Conflict resolution */
34    const CONFLICT_OVERWRITE_SERVER             = 0;
35    const CONFLICT_OVERWRITE_PIM                = 1;
36
37    /* TRUNCATION Constants */
38    const TRUNCATION_ALL                        = 0;
39    const TRUNCATION_1                          = 1;
40    const TRUNCATION_2                          = 2;
41    const TRUNCATION_3                          = 3;
42    const TRUNCATION_4                          = 4;
43    const TRUNCATION_5                          = 5;
44    const TRUNCATION_6                          = 6;
45    const TRUNCATION_7                          = 7;
46    const TRUNCATION_8                          = 8;
47    const TRUNCATION_9                          = 9;
48    const TRUNCATION_NONE                       = 9; // @deprecated
49
50    /* FOLDERHIERARCHY */
51    const FOLDERHIERARCHY_FOLDERS               = 'FolderHierarchy:Folders';
52    const FOLDERHIERARCHY_FOLDER                = 'FolderHierarchy:Folder';
53    const FOLDERHIERARCHY_DISPLAYNAME           = 'FolderHierarchy:DisplayName';
54    const FOLDERHIERARCHY_SERVERENTRYID         = 'FolderHierarchy:ServerEntryId';
55    const FOLDERHIERARCHY_PARENTID              = 'FolderHierarchy:ParentId';
56    const FOLDERHIERARCHY_TYPE                  = 'FolderHierarchy:Type';
57    const FOLDERHIERARCHY_RESPONSE              = 'FolderHierarchy:Response';
58    const FOLDERHIERARCHY_STATUS                = 'FolderHierarchy:Status';
59    const FOLDERHIERARCHY_CONTENTCLASS          = 'FolderHierarchy:ContentClass';
60    const FOLDERHIERARCHY_CHANGES               = 'FolderHierarchy:Changes';
61    const FOLDERHIERARCHY_SYNCKEY               = 'FolderHierarchy:SyncKey';
62    const FOLDERHIERARCHY_FOLDERSYNC            = 'FolderHierarchy:FolderSync';
63    const FOLDERHIERARCHY_COUNT                 = 'FolderHierarchy:Count';
64    const FOLDERHIERARCHY_VERSION               = 'FolderHierarchy:Version';
65
66    /* SYNC */
67    const SYNC_SYNCHRONIZE                      = 'Synchronize';
68    const SYNC_REPLIES                          = 'Replies';
69    const SYNC_ADD                              = 'Add';
70    const SYNC_MODIFY                           = 'Modify';
71    const SYNC_REMOVE                           = 'Remove';
72    const SYNC_FETCH                            = 'Fetch';
73    const SYNC_SYNCKEY                          = 'SyncKey';
74    const SYNC_CLIENTENTRYID                    = 'ClientEntryId';
75    const SYNC_SERVERENTRYID                    = 'ServerEntryId';
76    const SYNC_STATUS                           = 'Status';
77    const SYNC_FOLDER                           = 'Folder';
78    const SYNC_FOLDERTYPE                       = 'FolderType';
79    const SYNC_VERSION                          = 'Version';
80    const SYNC_FOLDERID                         = 'FolderId';
81    const SYNC_GETCHANGES                       = 'GetChanges';
82    const SYNC_MOREAVAILABLE                    = 'MoreAvailable';
83    const SYNC_WINDOWSIZE                       = 'WindowSize';
84    const SYNC_COMMANDS                         = 'Commands';
85    const SYNC_OPTIONS                          = 'Options';
86    const SYNC_FILTERTYPE                       = 'FilterType';
87    const SYNC_TRUNCATION                       = 'Truncation';
88    const SYNC_RTFTRUNCATION                    = 'RtfTruncation';
89    const SYNC_CONFLICT                         = 'Conflict';
90    const SYNC_FOLDERS                          = 'Folders';
91    const SYNC_DATA                             = 'Data';
92    const SYNC_DELETESASMOVES                   = 'DeletesAsMoves';
93    const SYNC_NOTIFYGUID                       = 'NotifyGUID';
94    const SYNC_SUPPORTED                        = 'Supported';
95    const SYNC_SOFTDELETE                       = 'SoftDelete';
96    const SYNC_MIMESUPPORT                      = 'MIMESupport';
97    const SYNC_MIMETRUNCATION                   = 'MIMETruncation';
98    const SYNC_NEWMESSAGE                       = 'NewMessage';
99    const SYNC_PARTIAL                          = 'Partial';
100    const SYNC_WAIT                             = 'Wait';
101    const SYNC_LIMIT                            = 'Limit';
102    // 14
103    const SYNC_HEARTBEATINTERVAL                = 'HeartbeatInterval';
104    const SYNC_CONVERSATIONMODE                 = 'ConversationMode';
105    const SYNC_MAXITEMS                         = 'MaxItems';
106
107    /* Document library */
108    const SYNC_DOCUMENTLIBRARY_LINKID           = 'DocumentLibrary:LinkId';
109    const SYNC_DOCUMENTLIBRARY_DISPLAYNAME      = 'DocumentLibrary:DisplayName';
110    const SYNC_DOCUMENTLIBRARY_ISFOLDER         = 'DocumentLibrary:IsFolder';
111    const SYNC_DOCUMENTLIBRARY_CREATIONDATE     = 'DocumentLibrary:CreationDate';
112    const SYNC_DOCUMENTLIBRARY_LASTMODIFIEDDATE = 'DocumentLibrary:LastModifiedDate';
113    const SYNC_DOCUMENTLIBRARY_ISHIDDEN         = 'DocumentLibrary:IsHidden';
114    const SYNC_DOCUMENTLIBRARY_CONTENTLENGTH    = 'DocumentLibrary:ContentLength';
115    const SYNC_DOCUMENTLIBRARY_CONTENTTYPE      = 'DocumentLibrary:ContentType';
116
117    /* AIRSYNCBASE */
118    const AIRSYNCBASE_BODYPREFERENCE            = 'AirSyncBase:BodyPreference';
119    const AIRSYNCBASE_TYPE                      = 'AirSyncBase:Type';
120    const AIRSYNCBASE_TRUNCATIONSIZE            = 'AirSyncBase:TruncationSize';
121    const AIRSYNCBASE_ALLORNONE                 = 'AirSyncBase:AllOrNone';
122    const AIRSYNCBASE_BODY                      = 'AirSyncBase:Body';
123    const AIRSYNCBASE_DATA                      = 'AirSyncBase:Data';
124    const AIRSYNCBASE_ESTIMATEDDATASIZE         = 'AirSyncBase:EstimatedDataSize';
125    const AIRSYNCBASE_TRUNCATED                 = 'AirSyncBase:Truncated';
126    const AIRSYNCBASE_ATTACHMENTS               = 'AirSyncBase:Attachments';
127    const AIRSYNCBASE_ATTACHMENT                = 'AirSyncBase:Attachment';
128    const AIRSYNCBASE_DISPLAYNAME               = 'AirSyncBase:DisplayName';
129    const AIRSYNCBASE_FILEREFERENCE             = 'AirSyncBase:FileReference';
130    const AIRSYNCBASE_METHOD                    = 'AirSyncBase:Method';
131    const AIRSYNCBASE_CONTENTID                 = 'AirSyncBase:ContentId';
132    const AIRSYNCBASE_CONTENTLOCATION           = 'AirSyncBase:ContentLocation';
133    const AIRSYNCBASE_ISINLINE                  = 'AirSyncBase:IsInline';
134    const AIRSYNCBASE_NATIVEBODYTYPE            = 'AirSyncBase:NativeBodyType';
135    const AIRSYNCBASE_CONTENTTYPE               = 'AirSyncBase:ContentType';
136    const AIRSYNCBASE_LOCATION                  = 'AirSyncBase:Location';
137
138    // 14.0
139    const AIRSYNCBASE_PREVIEW                   = 'AirSyncBase:Preview';
140
141    // 14.1
142    const AIRSYNCBASE_BODYPARTPREFERENCE        = 'AirSyncBase:BodyPartPreference';
143    const AIRSYNCBASE_BODYPART                  = 'AirSyncBase:BodyPart';
144    const AIRSYNCBASE_STATUS                    = 'AirSyncBase:Status';
145
146    // 16.0
147    const AIRSYNCBASE_ADD                       = 'AirSyncBase:Add';
148    const AIRSYNCBASE_DELETE                    = 'AirSyncBase:Delete';
149    const AIRSYNCBASE_CLIENTID                  = 'AirSyncBase:ClientId';
150    const AIRSYNCBASE_CONTENT                   = 'AirSyncBase:Content';
151    const AIRSYNCBASE_ANNOTATION                = 'AirSyncBase:Annotation';
152    const AIRSYNCBASE_STREET                    = 'AirSyncBase:Street';
153    const AIRSYNCBASE_CITY                      = 'AirSyncBase:City';
154    const AIRSYNCBASE_STATE                     = 'AirSyncBase:State';
155    const AIRSYNCBASE_COUNTRY                   = 'AirSyncBase:Country';
156    const AIRSYNCBASE_POSTALCODE                = 'AirSyncBase:PostalCode';
157    const AIRSYNCBASE_LATITUDE                  = 'AirSyncBase:Latitude';
158    const AIRSYNCBASE_LONGITUDE                 = 'AirSyncBase:Longitude';
159    const AIRSYNCBASE_ACCURACY                  = 'AirSyncBase:Accuracy';
160    const AIRSYNCBASE_ALTITUDE                  = 'AirSyncBase:Altitude';
161    const AIRSYNCBASE_ALTITUDEACCURACY          = 'AirSyncBase:AltitudeAccuracy';
162    const AIRSYNCBASE_LOCATIONURI               = 'AirSyncBase:LocationUri';
163    const AIRSYNCBASE_INSTANCEID                = 'AirSyncBase:InstanceId';
164
165
166    /* Body type prefs */
167    const BODYPREF_TYPE_PLAIN                   = 1;
168    const BODYPREF_TYPE_HTML                    = 2;
169    const BODYPREF_TYPE_RTF                     = 3;
170    const BODYPREF_TYPE_MIME                    = 4;
171
172    /* PROVISION */
173    const PROVISION_PROVISION                   =  'Provision:Provision';
174    const PROVISION_POLICIES                    =  'Provision:Policies';
175    const PROVISION_POLICY                      =  'Provision:Policy';
176    const PROVISION_POLICYTYPE                  =  'Provision:PolicyType';
177    const PROVISION_POLICYKEY                   =  'Provision:PolicyKey';
178    const PROVISION_DATA                        =  'Provision:Data';
179    const PROVISION_STATUS                      =  'Provision:Status';
180    const PROVISION_REMOTEWIPE                  =  'Provision:RemoteWipe';
181    const PROVISION_EASPROVISIONDOC             =  'Provision:EASProvisionDoc';
182
183    /* Policy types */
184    const POLICYTYPE_XML                        = 'MS-WAP-Provisioning-XML';
185    const POLICYTYPE_WBXML                      = 'MS-EAS-Provisioning-WBXML';
186
187    /* Flags */
188    // @TODO: H6 Change this to CHANGE_TYPE_NEW
189    const FLAG_NEWMESSAGE                       = 'NewMessage';
190
191    /* Folder types */
192    const FOLDER_TYPE_OTHER                     =  1;
193    const FOLDER_TYPE_INBOX                     =  2;
194    const FOLDER_TYPE_DRAFTS                    =  3;
195    const FOLDER_TYPE_WASTEBASKET               =  4;
196    const FOLDER_TYPE_SENTMAIL                  =  5;
197    const FOLDER_TYPE_OUTBOX                    =  6;
198    const FOLDER_TYPE_TASK                      =  7;
199    const FOLDER_TYPE_APPOINTMENT               =  8;
200    const FOLDER_TYPE_CONTACT                   =  9;
201    const FOLDER_TYPE_NOTE                      =  10;
202    const FOLDER_TYPE_JOURNAL                   =  11;
203    const FOLDER_TYPE_USER_MAIL                 =  12;
204    const FOLDER_TYPE_USER_APPOINTMENT          =  13;
205    const FOLDER_TYPE_USER_CONTACT              =  14;
206    const FOLDER_TYPE_USER_TASK                 =  15;
207    const FOLDER_TYPE_USER_JOURNAL              =  16;
208    const FOLDER_TYPE_USER_NOTE                 =  17;
209    const FOLDER_TYPE_UNKNOWN                   =  18;
210    const FOLDER_TYPE_RECIPIENT_CACHE           =  19;
211    // @TODO, remove const definition in H6, not used anymore.
212    const FOLDER_TYPE_DUMMY                     =  999999;
213
214    /* Origin of changes **/
215    const CHANGE_ORIGIN_PIM                     = 0;
216    const CHANGE_ORIGIN_SERVER                  = 1;
217    const CHANGE_ORIGIN_NA                      = 3;
218
219    /* Remote wipe **/
220    const RWSTATUS_NA                           = 0;
221    const RWSTATUS_OK                           = 1;
222    const RWSTATUS_PENDING                      = 2;
223    const RWSTATUS_WIPED                        = 3;
224
225    /* GAL **/
226    const GAL_DISPLAYNAME                       = 'GAL:DisplayName';
227    const GAL_PHONE                             = 'GAL:Phone';
228    const GAL_OFFICE                            = 'GAL:Office';
229    const GAL_TITLE                             = 'GAL:Title';
230    const GAL_COMPANY                           = 'GAL:Company';
231    const GAL_ALIAS                             = 'GAL:Alias';
232    const GAL_FIRSTNAME                         = 'GAL:FirstName';
233    const GAL_LASTNAME                          = 'GAL:LastName';
234    const GAL_HOMEPHONE                         = 'GAL:HomePhone';
235    const GAL_MOBILEPHONE                       = 'GAL:MobilePhone';
236    const GAL_EMAILADDRESS                      = 'GAL:EmailAddress';
237    // 14.1
238    const GAL_PICTURE                           = 'GAL:Picture';
239    const GAL_STATUS                            = 'GAL:Status';
240    const GAL_DATA                              = 'GAL:Data';
241
242    /* Request Type */
243    const REQUEST_TYPE_SYNC                     = 'sync';
244    const REQUEST_TYPE_FOLDERSYNC               = 'foldersync';
245
246    /* Change Type */
247    const CHANGE_TYPE_CHANGE                    = 'change';
248    const CHANGE_TYPE_DELETE                    = 'delete';
249    const CHANGE_TYPE_FLAGS                     = 'flags';
250    const CHANGE_TYPE_MOVE                      = 'move';
251    const CHANGE_TYPE_FOLDERSYNC                = 'foldersync';
252    const CHANGE_TYPE_SOFTDELETE                = 'softdelete';
253
254    // @since 2.36.0
255    const CHANGE_TYPE_DRAFT                     = 'draft';
256
257    /* Internal flags to indicate change is a change in reply/forward state */
258    const CHANGE_REPLY_STATE                    = '@--reply--@';
259    const CHANGE_REPLYALL_STATE                 = '@--replyall--@';
260    const CHANGE_FORWARD_STATE                  = '@--forward--@';
261
262    /* RM */
263    const RM_SUPPORT                            = 'RightsManagement:RightsManagementSupport';
264    const RM_TEMPLATEID                         = 'RightsManagement:TemplateId';
265
266    /* Collection Classes */
267    const CLASS_EMAIL                           = 'Email';
268    const CLASS_CONTACTS                        = 'Contacts';
269    const CLASS_CALENDAR                        = 'Calendar';
270    const CLASS_TASKS                           = 'Tasks';
271    const CLASS_NOTES                           = 'Notes';
272    const CLASS_SMS                             = 'SMS';
273
274    /* Filtertype constants */
275    const FILTERTYPE_ALL                        = 0;
276    const FILTERTYPE_1DAY                       = 1;
277    const FILTERTYPE_3DAYS                      = 2;
278    const FILTERTYPE_1WEEK                      = 3;
279    const FILTERTYPE_2WEEKS                     = 4;
280    const FILTERTYPE_1MONTH                     = 5;
281    const FILTERTYPE_3MONTHS                    = 6;
282    const FILTERTYPE_6MONTHS                    = 7;
283    const FILTERTYPE_INCOMPLETETASKS            = 8;
284
285    // @todo normalize to string values.
286    const PROVISIONING_FORCE                    = true;
287    const PROVISIONING_LOOSE                    = 'loose';
288    const PROVISIONING_NONE                     = false;
289
290    const FOLDER_ROOT                           = 0;
291
292    const VERSION_TWOFIVE                       = '2.5';
293    const VERSION_TWELVE                        = '12.0';
294    const VERSION_TWELVEONE                     = '12.1';
295    const VERSION_FOURTEEN                      = '14.0';
296    const VERSION_FOURTEENONE                   = '14.1';
297    const VERSION_SIXTEEN                       = '16.0';
298
299    const MIME_SUPPORT_NONE                     = 0;
300    const MIME_SUPPORT_SMIME                    = 1;
301    const MIME_SUPPORT_ALL                      = 2;
302
303    const IMAP_FLAG_REPLY                       = 'reply';
304    const IMAP_FLAG_FORWARD                     = 'forward';
305
306    /* Result Type */
307    const RESOLVE_RESULT_GAL                    = 1;
308    const RESOLVE_RESULT_ADDRESSBOOK            = 2;
309
310    /* Auth failure reasons */
311    const AUTH_REASON_USER_DENIED               = 'user';
312    const AUTH_REASON_DEVICE_DENIED             = 'device';
313
314    /* Internal flag indicates all possible fields are ghosted */
315    const ALL_GHOSTED                           = 'allghosted';
316
317    const LIBRARY_VERSION                       = '2.41.4';
318
319    /**
320     * Logger
321     *
322     * @var Horde_ActiveSync_Interface_LoggerFactory
323     */
324    protected $_loggerFactory;
325
326    /**
327     * The logger for this class.
328     *
329     * @var Horde_Log_Logger
330     */
331    protected static $_logger;
332
333    /**
334     * Provisioning support
335     *
336     * @var string
337     */
338    protected $_provisioning;
339
340    /**
341     * Highest version to support.
342     *
343     * @var float
344     */
345    protected $_maxVersion = self::VERSION_SIXTEEN;
346
347    /**
348     * The actual version we are supporting.
349     *
350     * @var float
351     */
352    protected static $_version;
353
354    /**
355     * Multipart support?
356     *
357     * @var boolean
358     */
359    protected $_multipart = false;
360
361    /**
362     * Support gzip compression of certain data parts?
363     *
364     * @var boolean
365     */
366    protected $_compression = false;
367
368    /**
369     * Local cache of Get variables/decoded base64 uri
370     *
371     * @var array
372     */
373    protected $_get = array();
374
375    /**
376     * Path to root certificate bundle
377     *
378     * @var string
379     */
380    protected $_certPath;
381
382    /**
383     *
384     * @var Horde_ActiveSync_Device
385     */
386    protected static $_device;
387
388    /**
389     * Wbxml encoder
390     *
391     * @var Horde_ActiveSync_Wbxml_Encoder
392     */
393    protected $_encoder;
394
395    /**
396     * Wbxml decoder
397     *
398     * @var Horde_ActiveSync_Wbxml_Decoder
399     */
400    protected $_decoder;
401
402    /**
403     * The singleton collections handler.
404     *
405     * @var Horde_ActiveSync_Collections
406     */
407    protected $_collectionsObj;
408
409    /**
410     * Global error flag.
411     *
412     * @var boolean
413     */
414    protected $_globalError = false;
415
416    /**
417     * Process id (used in logging).
418     *
419     * @var integer
420     */
421    protected $_procid;
422
423    /**
424     * Flag to  indicate we need to update the device version.
425     *
426     * @var boolean
427     */
428    protected $_needMsRp = false;
429
430    /**
431     * Supported EAS versions.
432     *
433     * @var array
434     */
435    protected static $_supportedVersions = array(
436        self::VERSION_TWOFIVE,
437        self::VERSION_TWELVE,
438        self::VERSION_TWELVEONE,
439        self::VERSION_FOURTEEN,
440        self::VERSION_FOURTEENONE,
441        self::VERSION_SIXTEEN
442    );
443
444    /**
445     * Factory method for creating Horde_ActiveSync_Message objects.
446     *
447     * @param string $message  The message type.
448     * @since 2.4.0
449     *
450     * @return Horde_ActiveSync_Message_Base   The concrete message object.
451     * @todo For H6, move to Horde_ActiveSync_Message_Base::factory()
452     */
453    public static function messageFactory($message)
454    {
455        $class = 'Horde_ActiveSync_Message_' . $message;
456        if (!class_exists($class)) {
457            throw new InvalidArgumentException(sprintf('Class %s does not exist.', $class));
458        }
459
460        return new $class(array(
461            'logger' => self::$_logger,
462            'protocolversion' => self::$_version,
463            'device' => self::$_device));
464    }
465
466    /**
467     * Const'r
468     *
469     * @param Horde_ActiveSync_Driver_Base $driver      The backend driver.
470     * @param Horde_ActiveSync_Wbxml_Decoder $decoder   The Wbxml decoder.
471     * @param Horde_ActiveSync_Wbxml_Endcoder $encoder  The Wbxml encoder.
472     * @param Horde_ActiveSync_State_Base $state        The state driver.
473     * @param Horde_Controller_Request_Http $request    The HTTP request object.
474     *
475     * @return Horde_ActiveSync  The ActiveSync server object.
476     */
477    public function __construct(
478        Horde_ActiveSync_Driver_Base $driver,
479        Horde_ActiveSync_Wbxml_Decoder $decoder,
480        Horde_ActiveSync_Wbxml_Encoder $encoder,
481        Horde_ActiveSync_State_Base $state,
482        Horde_Controller_Request_Http $request)
483    {
484        // The http request
485        $this->_request = $request;
486
487        // Backend driver
488        $this->_driver = $driver;
489        $this->_driver->setProtocolVersion($this->getProtocolVersion());
490
491        // Device state manager
492        $this->_state = $state;
493
494        // Wbxml handlers
495        $this->_encoder = $encoder;
496        $this->_decoder = $decoder;
497
498        $this->_procid = getmypid();
499    }
500
501    /**
502     * Return a collections singleton.
503     *
504     * @return Horde_ActiveSync_Collections
505     * @since 2.4.0
506     */
507    public function getCollectionsObject()
508    {
509        if (empty($this->_collectionsObj)) {
510            $this->_collectionsObj = new Horde_ActiveSync_Collections($this->getSyncCache(), $this);
511        }
512
513        return $this->_collectionsObj;
514    }
515
516    /**
517     * Return a new, fully configured SyncCache.
518     *
519     * @return Horde_ActiveSync_SyncCache
520     * @since 2.4.0
521     */
522    public function getSyncCache()
523    {
524        return new Horde_ActiveSync_SyncCache(
525            $this->_state,
526            self::$_device->id,
527            self::$_device->user,
528            self::$_logger
529        );
530    }
531
532    /**
533     * Return an Importer object.
534     *
535     * @return Horde_ActiveSync_Connector_Importer
536     * @since 2.4.0
537     */
538    public function getImporter()
539    {
540        $importer = new Horde_ActiveSync_Connector_Importer($this);
541        $importer->setLogger(self::$_logger);
542
543        return $importer;
544    }
545
546    /**
547     * Authenticate to the backend.
548     *
549     * @param Horde_ActiveSync_Credentials $credentials  The credentials object.
550     *
551     * @return boolean  True on successful authentication to the backend.
552     * @throws Horde_ActiveSync_Exception
553     */
554    public function authenticate(Horde_ActiveSync_Credentials $credentials)
555    {
556        if (!$credentials->username) {
557            // No provided username or Authorization header.
558            self::$_logger->notice('Client did not provide authentication data.');
559            return false;
560        }
561
562        $user = $this->_driver->getUsernameFromEmail($credentials->username);
563        $pos = strrpos($user, '\\');
564        if ($pos !== false) {
565            $domain = substr($user, 0, $pos);
566            $user = substr($user, $pos + 1);
567        } else {
568            $domain = null;
569        }
570
571        // Authenticate
572        if ($result = $this->_driver->authenticate($user, $credentials->password, $domain)) {
573            if ($result === self::AUTH_REASON_USER_DENIED) {
574                $this->_globalError = Horde_ActiveSync_Status::SYNC_NOT_ALLOWED;
575            } elseif ($result === self::AUTH_REASON_DEVICE_DENIED) {
576                $this->_globalError = Horde_ActiveSync_Status::DEVICE_BLOCKED_FOR_USER;
577            } elseif ($result !== true) {
578                $this->_globalError = Horde_ActiveSync_Status::DENIED;
579            }
580        } else {
581            return false;
582        }
583
584        if (!$this->_driver->setup($user)) {
585            return false;
586        }
587
588        return true;
589    }
590
591    /**
592     * Allow to force the highest version to support.
593     *
594     * @param float $version  The highest version
595     */
596    public function setSupportedVersion($version)
597    {
598        $this->_maxVersion = $version;
599    }
600
601    /**
602     * Set the local path to the root certificate bundle.
603     *
604     * @param string $path  The local path to the bundle.
605     */
606    public function setRootCertificatePath($path)
607    {
608        $this->_certPath = $path;
609    }
610
611    /**
612     * Getter
613     *
614     * @param string $property  The property to return.
615     *
616     * @return mixed  The value of the requested property.
617     */
618    public function __get($property)
619    {
620        switch ($property) {
621        case 'encoder':
622        case 'decoder':
623        case 'state':
624        case 'request':
625        case 'driver':
626        case 'provisioning':
627        case 'multipart':
628        case 'certPath':
629            $property = '_' . $property;
630            return $this->$property;
631        case 'logger':
632            return self::$_logger;
633        case 'device':
634            return self::$_device;
635        default:
636            throw new InvalidArgumentException(sprintf(
637                'The property %s does not exist',
638                $property)
639            );
640        }
641    }
642
643    /**
644     * Setter for the logger factory.
645     *
646     * @param Horde_ActiveSync_Interface_LoggerFactory $logger  The logger factory.
647     */
648    public function setLogger(Horde_ActiveSync_Interface_LoggerFactory $logger)
649    {
650        $this->_loggerFactory = $logger;
651    }
652
653    /**
654     * Instantiate the logger from the factory and inject into all needed
655     * objects.
656     *
657     * @param array $options [description]
658     */
659    protected function _setLogger(array $options)
660    {
661        if (!empty($this->_loggerFactory)) {
662            // @TODO. Remove wrapper.
663            self::$_logger = self::_wrapLogger($this->_loggerFactory->create($options));
664            $this->_encoder->setLogger(self::$_logger);
665            $this->_decoder->setLogger(self::$_logger);
666            $this->_driver->setLogger(self::$_logger);
667            $this->_state->setLogger(self::$_logger);
668        }
669    }
670
671    public static function _wrapLogger(Horde_Log_Logger $logger)
672    {
673        if (!($logger instanceof Horde_ActiveSync_Log_Logger)) {
674            return new Horde_ActiveSync_Log_Logger_Deprecated(null, $logger);
675        }
676
677        return $logger;
678    }
679
680    /**
681     * Setter for provisioning support
682     *
683     */
684    public function setProvisioning($provision)
685    {
686        $this->_provisioning = $provision;
687    }
688
689    /**
690     * Send the headers indicating that provisioning is required.
691     */
692    public function provisioningRequired()
693    {
694        $this->provisionHeader();
695        $this->activeSyncHeader();
696        $this->versionHeader();
697        $this->commandsHeader();
698        header('Cache-Control: private');
699    }
700
701    /**
702     * The heart of the server. Dispatch a request to the appropriate request
703     * handler.
704     *
705     * @param string $cmd    The command we are requesting.
706     * @param string $devId  The device id making the request. @deprecated
707     *
708     * @return string|boolean  false if failed, true if succeeded and response
709     *                         content is wbxml, otherwise the
710     *                         content-type string to send in the response.
711     * @throws Horde_ActiveSync_Exception
712     * @throws Horde_ActiveSync_Exception_InvalidRequest
713     * @throws Horde_ActiveSync_PermissionDenied
714     */
715    public function handleRequest($cmd, $devId)
716    {
717        $get = $this->getGetVars();
718        if (empty($cmd)) {
719            $cmd = $get['Cmd'];
720        }
721        if (empty($devId)) {
722            $devId = !empty($get['DeviceId']) ? Horde_String::upper($get['DeviceId']) : null;
723        } else {
724            $devId = Horde_String::upper($devId);
725        }
726        $this->_setLogger($get);
727
728        // @TODO: Remove is_callable check for H6.
729        // Callback to give the backend the option to limit EAS version based
730        // on user/device/etc...
731        if (is_callable(array($this->_driver, 'versionCallback'))) {
732            $this->_driver->versionCallback($this);
733        }
734
735        // Autodiscovery handles authentication on it's own.
736        if ($cmd == 'Autodiscover') {
737            $request = new Horde_ActiveSync_Request_Autodiscover($this, new Horde_ActiveSync_Device($this->_state));
738
739            if (!empty(self::$_logger)) {
740                $request->setLogger(self::$_logger);
741            }
742
743            $result = $request->handle($this->_request);
744            $this->_driver->clearAuthentication();
745            return $result;
746        }
747        if (!$this->authenticate(new Horde_ActiveSync_Credentials($this))) {
748            $this->activeSyncHeader();
749            $this->versionHeader();
750            $this->commandsHeader();
751            throw new Horde_Exception_AuthenticationFailure();
752        }
753
754        self::$_logger->info(sprintf(
755            '%s%s request received for user %s',
756            str_repeat('-', 10),
757            Horde_String::upper($cmd),
758            $this->_driver->getUser())
759        );
760
761        // These are all handled in the same class.
762        if ($cmd == 'FolderDelete' || $cmd == 'FolderUpdate') {
763            $cmd = 'FolderCreate';
764        }
765
766        // Device id is REQUIRED
767        if (empty($devId)) {
768            if ($cmd == 'Options') {
769                $this->_handleOptionsRequest();
770                $this->_driver->clearAuthentication();
771                return true;
772            }
773            $this->_driver->clearAuthentication();
774            throw new Horde_ActiveSync_Exception_InvalidRequest('Device failed to send device id.');
775        }
776
777        // EAS Version
778        $version = $this->getProtocolVersion();
779
780        // Device. Even though versions of EAS > 12.1 are supposed to send
781        // EAS status codes back to indicate various errors in allowing a client
782        // to connect, we just throw an exception (thus causing a HTTP error
783        // code to be sent as in versions 12.1 and below). Until we refactor for
784        // Horde 6, we don't know the response type to wrap the status code in
785        // until we load the request handler, which requires we start to parse
786        // the WBXML stream and device information etc... This saves resources
787        // as well as keeps things cleaner until we refactor.
788        $device_result = $this->_handleDevice($devId);
789
790        // Don't bother with everything else if all we want are Options
791        if ($cmd == 'Options') {
792            $this->_handleOptionsRequest();
793            $this->_driver->clearAuthentication();
794            return true;
795        }
796
797        // Set provisioning support now that we are authenticated.
798        $this->setProvisioning($this->_driver->getProvisioning(self::$_device));
799
800        // Read the initial Wbxml header
801        $this->_decoder->readWbxmlHeader();
802
803        // Support Multipart response for ITEMOPERATIONS requests?
804        $headers = $this->_request->getHeaders();
805        if ((!empty($headers['ms-asacceptmultipart']) && $headers['ms-asacceptmultipart'] == 'T') ||
806            !empty($get['AcceptMultiPart'])) {
807            $this->_multipart = true;
808            self::$_logger->info('Requesting multipart data.');
809        }
810
811        // Load the request handler to handle the request
812        // We must send the EAS header here, since some requests may start
813        // output and be large enough to flush the buffer (e.g., GetAttachment)
814        // See Bug: 12486
815        $this->activeSyncHeader();
816        if ($cmd != 'GetAttachment') {
817            $this->contentTypeHeader();
818        }
819
820        // Should we announce a new version is available to the client?
821        if (!empty($this->_needMsRp)) {
822            self::$_logger->info('Announcing X-MS-RP to client.');
823            header("X-MS-RP: ". $this->getSupportedVersions());
824        }
825
826        // @TODO: Look at getting rid of having to set the version in the driver
827        //        and get it from the device object for H6.
828        $this->_driver->setDevice(self::$_device);
829        $class = 'Horde_ActiveSync_Request_' . basename($cmd);
830        if (class_exists($class)) {
831            $request = new $class($this);
832            $request->setLogger(self::$_logger);
833            $result = $request->handle();
834            self::$_logger->info(sprintf(
835                'Maximum memory usage for ActiveSync request: %d bytes.',
836                memory_get_peak_usage(true))
837            );
838
839            return $result;
840        }
841
842        $this->_driver->clearAuthentication();
843        throw new Horde_ActiveSync_Exception_InvalidRequest(basename($cmd) . ' not supported.');
844    }
845
846    /**
847     * Handle device checks. Takes into account permissions and restrictions
848     * via various callback methods.
849     *
850     * @param  string $devId  The client provided device id.
851     *
852     * @return boolean  If EAS version is > 12.1 returns false on any type of
853     *                  failure in allowing the device to connect. Sets
854     *                  appropriate internal variables to indicate the type of
855     *                  error to return to the client. Failure on EAS version
856     *                  < 12.1 results in throwing exceptions. Otherwise, return
857     *                  true.
858     * @throws Horde_ActiveSync_Exception, Horde_Exception_AuthenticationFailure
859     */
860    protected function _handleDevice($devId)
861    {
862        $get = $this->getGetVars();
863        $version = $this->getProtocolVersion();
864
865        // Does device exist AND does the user have an account on the device?
866        if (!$this->_state->deviceExists($devId, $this->_driver->getUser())) {
867            // Device might exist, but with a new (additional) user account
868            if ($this->_state->deviceExists($devId)) {
869                self::$_device = $this->_state->loadDeviceInfo($devId);
870            } else {
871                self::$_device = new Horde_ActiveSync_Device($this->_state);
872            }
873            self::$_device->policykey = 0;
874            self::$_device->userAgent = $this->_request->getHeader('User-Agent');
875            self::$_device->deviceType = !empty($get['DeviceType']) ? $get['DeviceType'] : '';
876            self::$_device->rwstatus = self::RWSTATUS_NA;
877            self::$_device->user = $this->_driver->getUser();
878            self::$_device->id = $devId;
879            self::$_device->needsVersionUpdate($this->getSupportedVersions());
880            self::$_device->version = $version;
881            // @TODO: Remove is_callable check for H6.
882            //        Combine this with the modifyDevice callback? Allow $device
883            //        to be modified here?
884            if (is_callable(array($this->_driver, 'createDeviceCallback'))) {
885                $callback_ret = $this->_driver->createDeviceCallback(self::$_device);
886                if ($callback_ret !== true) {
887                    $msg = sprintf(
888                        'The device %s was disallowed for user %s per policy settings.',
889                        self::$_device->id,
890                        self::$_device->user);
891                    self::$_logger->err($msg);
892                    // Always throw exception in place of status code since we
893                    // won't have a version number before the device is created.
894                    throw new Horde_Exception_AuthenticationFailure($msg, $callback_ret);
895                } else {
896                    // Give the driver a chance to modify device properties.
897                    if (is_callable(array($this->_driver, 'modifyDeviceCallback'))) {
898                        self::$_device = $this->_driver->modifyDeviceCallback(self::$_device);
899                    }
900                }
901            }
902        } else {
903            self::$_device = $this->_state->loadDeviceInfo($devId, $this->_driver->getUser());
904
905            // If the device state was removed from storage, we may lose the
906            // device properties, so try to repopulate what we can. userAgent
907            // is ALWAYS available, so if it's missing, the state is gone.
908            if (empty(self::$_device->userAgent)) {
909                self::$_device->userAgent = $this->_request->getHeader('User-Agent');
910                self::$_device->deviceType = !empty($get['DeviceType']) ? $get['DeviceType'] : '';
911                self::$_device->user = $this->_driver->getUser();
912            }
913
914            if (empty(self::$_device->version)) {
915                self::$_device->version = $version;
916            }
917            if (self::$_device->version < $this->_maxVersion &&
918                self::$_device->needsVersionUpdate($this->getSupportedVersions())) {
919                $this->_needMsRp = true;
920            }
921
922            // Give the driver a chance to modify device properties.
923            if (is_callable(array($this->_driver, 'modifyDeviceCallback'))) {
924                self::$_device = $this->_driver->modifyDeviceCallback(self::$_device);
925            }
926        }
927
928        // Save the device now that we know it is at least allowed to connect,
929        // or it has connected successfully at least once in the past.
930        self::$_device->save();
931        if (is_callable(array($this->_driver, 'deviceCallback'))) {
932            $callback_ret = $this->_driver->deviceCallback(self::$_device);
933            if ($callback_ret !== true) {
934                $msg = sprintf(
935                    'The device %s was disallowed for user %s per policy settings.',
936                    self::$_device->id,
937                    self::$_device->user);
938                self::$_logger->err($msg);
939                if ($version > self::VERSION_TWELVEONE) {
940                    // Use a status code here, since the device has already
941                    // connected.
942                    $this->_globalError = $callback_ret;
943                    return false;
944                } else {
945                    throw new Horde_Exception_AuthenticationFailure($msg, $callback_ret);
946                }
947            }
948        }
949
950        // Lastly, check if the device has been set to blocked.
951        if (self::$_device->blocked) {
952            $msg = sprintf(
953                'The device %s was blocked.',
954                self::$_device->id);
955            self::$_logger->err($msg);
956            if ($version > self::VERSION_TWELVEONE) {
957                $this->_globalError = Horde_ActiveSync_Status::DEVICE_BLOCKED_FOR_USER;
958                return false;
959            } else {
960                throw new Horde_ActiveSync_Exception($msg);
961            }
962        }
963
964        return true;
965    }
966
967    /**
968     * Send the MS_Server-ActiveSync header.
969     *
970     * @return array Returns an array of the headers that were sent.
971     *     @since  2.39.0
972     */
973    public function activeSyncHeader()
974    {
975        $headers = array(
976            'Allow: OPTIONS,POST',
977            sprintf('Server: Horde_ActiveSync Library v%s', self::LIBRARY_VERSION),
978            'Public: OPTIONS,POST'
979        );
980
981        switch ($this->_maxVersion) {
982        case self::VERSION_TWOFIVE:
983            $headers[] = 'MS-Server-ActiveSync: 6.5.7638.1';
984            break;
985        case self::VERSION_TWELVE:
986            $headers[] = 'MS-Server-ActiveSync: 12.0';
987            break;
988        case self::VERSION_TWELVEONE:
989            $headers[] = 'MS-Server-ActiveSync: 12.1';
990            break;
991        case self::VERSION_FOURTEEN:
992            $headers[] = 'MS-Server-ActiveSync: 14.0';
993            break;
994        case self::VERSION_FOURTEENONE:
995            $headers[] = 'MS-Server-ActiveSync: 14.1';
996            break;
997        case self::VERSION_SIXTEEN:
998            $headers[] = 'MS-Server-ActiveSync: 16.0';
999        }
1000
1001        foreach ($headers as $hdr) {
1002            header($hdr);
1003        }
1004
1005        return $headers;
1006    }
1007
1008    /**
1009     * Send the protocol versions header.
1010     *
1011     * @return  string  The header that was sent. @since 2.39.0
1012     */
1013    public function versionHeader()
1014    {
1015        $hdr = sprintf('MS-ASProtocolVersions: %s', $this->getSupportedVersions());
1016        header($hdr);
1017
1018        return $hdr;
1019    }
1020
1021    /**
1022     * Return supported versions in a comma delimited string suitable for
1023     * sending as the MS-ASProtocolVersions header.
1024     *
1025     * @return string
1026     */
1027    public function getSupportedVersions()
1028    {
1029        return implode(',', array_slice(self::$_supportedVersions, 0, (array_search($this->_maxVersion, self::$_supportedVersions) + 1)));
1030    }
1031
1032    /**
1033     * Send protocol commands header.
1034     *
1035     * @return string  The header that was sent. @since 2.39.0
1036     */
1037    public function commandsHeader()
1038    {
1039        $hdr = sprintf('MS-ASProtocolCommands: %s', $this->getSupportedCommands());
1040        header($hdr);
1041
1042        return $hdr;
1043    }
1044
1045    /**
1046     * Return the supported commands in a comma delimited string suitable for
1047     * sending as the MS-ASProtocolCommands header.
1048     *
1049     * @return string
1050     */
1051    public function getSupportedCommands()
1052    {
1053        switch ($this->_maxVersion) {
1054        case self::VERSION_TWOFIVE:
1055            return 'Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,ResolveRecipients,ValidateCert,Provision,Search,Ping';
1056
1057        case self::VERSION_TWELVE:
1058        case self::VERSION_TWELVEONE:
1059        case self::VERSION_FOURTEEN:
1060        case self::VERSION_FOURTEENONE:
1061        case self::VERSION_SIXTEEN:
1062            return 'Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,Search,Settings,Ping,ItemOperations,Provision,ResolveRecipients,ValidateCert';
1063        }
1064    }
1065
1066    /**
1067     * Send provision header
1068     */
1069    public function provisionHeader()
1070    {
1071        header('HTTP/1.1 449 Retry after sending a PROVISION command');
1072    }
1073
1074    /**
1075     * Obtain the policy key header from the request.
1076     *
1077     * @return integer  The policy key or '0' if not set.
1078     */
1079    public function getPolicyKey()
1080    {
1081        // Policy key can come from header or encoded request parameters.
1082        $this->_policykey = $this->_request->getHeader('X-MS-PolicyKey');
1083        if (empty($this->_policykey)) {
1084            $get = $this->getGetVars();
1085            if (!empty($get['PolicyKey'])) {
1086                $this->_policykey = $get['PolicyKey'];
1087            } else {
1088                $this->_policykey = 0;
1089            }
1090        }
1091
1092        return $this->_policykey;
1093    }
1094
1095    /**
1096     * Obtain the ActiveSync protocol version requested by the client headers.
1097     *
1098     * @return string  The EAS version requested by the client.
1099     */
1100    public function getProtocolVersion()
1101    {
1102        if (!isset(self::$_version)) {
1103            self::$_version = $this->_request->getHeader('MS-ASProtocolVersion');
1104            if (empty(self::$_version)) {
1105                $get = $this->getGetVars();
1106                self::$_version = empty($get['ProtVer']) ? '1.0' : $get['ProtVer'];
1107            }
1108        }
1109        return self::$_version;
1110    }
1111
1112    /**
1113     * Return the GET variables passed from the device, decoding from
1114     * base64 if needed.
1115     *
1116     * @return array  A hash of get variables => values.
1117     */
1118    public function getGetVars()
1119    {
1120        if (!empty($this->_get)) {
1121            return $this->_get;
1122        }
1123
1124        $results = array();
1125        $get = $this->_request->getGetVars();
1126
1127        // Do we need to decode the request parameters?
1128        if (!isset($get['Cmd']) && !isset($get['DeviceId']) && !isset($get['DeviceType'])) {
1129            $serverVars = $this->_request->getServerVars();
1130            if (isset($serverVars['QUERY_STRING']) && strlen($serverVars['QUERY_STRING']) >= 10) {
1131                $results = Horde_ActiveSync_Utils::decodeBase64($serverVars['QUERY_STRING']);
1132                // Normalize values.
1133                switch ($results['DeviceType']) {
1134                case 'PPC':
1135                    $results['DeviceType'] = 'PocketPC';
1136                    break;
1137                case 'SP':
1138                    $results['DeviceType'] = 'SmartPhone';
1139                    break;
1140                case 'WP':
1141                case 'WP8':
1142                    $results['DeviceType'] = 'WindowsPhone';
1143                    break;
1144                case 'android':
1145                case 'android40':
1146                    $results['DeviceType'] = 'android';
1147                }
1148                $this->_get = $results;
1149            }
1150        } else {
1151            $this->_get = $get;
1152        }
1153
1154        return $this->_get;
1155    }
1156
1157    /**
1158     * Return any global errors that occured during initial connection.
1159     *
1160     * @since 2.4.0
1161     * @return mixed  A Horde_ActiveSync_Status:: constant of boolean false if
1162     *                no errors.
1163     */
1164    public function checkGlobalError()
1165    {
1166        return $this->_globalError;
1167    }
1168
1169    /**
1170     * Send the content type header.
1171     *
1172     */
1173    public function contentTypeHeader($content_type = null)
1174    {
1175        if (!empty($content_type)) {
1176            header('Content-Type: ' . $content_type);
1177            return;
1178        }
1179        if ($this->_multipart) {
1180            header('Content-Type: application/vnd.ms-sync.multipart');
1181        } else {
1182            header('Content-Type: application/vnd.ms-sync.wbxml');
1183        }
1184    }
1185
1186    /**
1187     * Send the OPTIONS request response headers.
1188     */
1189    protected function _handleOptionsRequest()
1190    {
1191        $as_headers = implode("\r\n", $this->activeSyncHeader());
1192        $version_header = $this->versionHeader();
1193        $cmd_header = $this->commandsHeader();
1194        self::$_logger->meta(sprintf(
1195            "Returning OPTIONS response:\r\n%s\r\n%s\r\n%s",
1196            $as_headers, $version_header, $cmd_header)
1197        );
1198    }
1199
1200    /**
1201     * Return the number of bytes corresponding to the requested trunction
1202     * constant. This applies to MIMETRUNCATION only.
1203     *
1204     * @param integer $truncation  The constant.
1205     *
1206     * @return integer|boolean  Either the size, in bytes, to truncate or
1207     *                          falso if no truncation.
1208     * @since 2.20.0
1209     */
1210    public static function getMIMETruncSize($truncation)
1211    {
1212        switch($truncation) {
1213        case Horde_ActiveSync::TRUNCATION_ALL:
1214            return 0;
1215        case Horde_ActiveSync::TRUNCATION_1:
1216            return 4096;
1217        case Horde_ActiveSync::TRUNCATION_2:
1218            return 5120;
1219        case Horde_ActiveSync::TRUNCATION_3:
1220            return 7168;
1221        case Horde_ActiveSync::TRUNCATION_4:
1222            return 10240;
1223        case Horde_ActiveSync::TRUNCATION_5:
1224            return 20480;
1225        case Horde_ActiveSync::TRUNCATION_6:
1226            return 51200;
1227        case Horde_ActiveSync::TRUNCATION_7:
1228            return 102400;
1229        case Horde_ActiveSync::TRUNCATION_8:
1230            return false;
1231        default:
1232            return 1024; // Default to 1Kb
1233        }
1234    }
1235
1236    /**
1237     * Return the number of bytes corresponding to the requested trunction
1238     * constant.
1239     *
1240     * @param integer $truncation  The constant.
1241     *
1242     * @return integer|boolean  Either the size, in bytes, to truncate or
1243     *                          falso if no truncation.
1244     *
1245     */
1246    public static function getTruncSize($truncation)
1247    {
1248        switch($truncation) {
1249        case Horde_ActiveSync::TRUNCATION_ALL:
1250            return 0;
1251        case Horde_ActiveSync::TRUNCATION_1:
1252            return 512;
1253        case Horde_ActiveSync::TRUNCATION_2:
1254            return 1024;
1255        case Horde_ActiveSync::TRUNCATION_3:
1256            return 2048;
1257        case Horde_ActiveSync::TRUNCATION_4:
1258            return 5120;
1259        case Horde_ActiveSync::TRUNCATION_5:
1260            return 10240;
1261        case Horde_ActiveSync::TRUNCATION_6:
1262            return 20480;
1263        case Horde_ActiveSync::TRUNCATION_7:
1264            return 51200;
1265        case Horde_ActiveSync::TRUNCATION_8:
1266            return 102400;
1267        case Horde_ActiveSync::TRUNCATION_9:
1268        case Horde_ActiveSync::TRUNCATION_NONE: // @deprecated
1269            return false;
1270        default:
1271            return 1024; // Default to 1Kb
1272        }
1273    }
1274
1275}
1276