1276 lines
46 KiB
PHP
1276 lines
46 KiB
PHP
<?php
|
|
/**
|
|
* @license http://www.horde.org/licenses/gpl GPLv2
|
|
*
|
|
* @copyright 2009-2020 Horde LLC (http://www.horde.org)
|
|
* @author Michael J Rubinsky <mrubinsk@horde.org>
|
|
* @package ActiveSync
|
|
*/
|
|
|
|
/**
|
|
* The Horde ActiveSync server. Entry point for performing all ActiveSync
|
|
* operations.
|
|
*
|
|
* @license http://www.horde.org/licenses/gpl GPLv2
|
|
*
|
|
* @copyright 2009-2020 Horde LLC (http://www.horde.org)
|
|
* @author Michael J Rubinsky <mrubinsk@horde.org>
|
|
* @package ActiveSync
|
|
*
|
|
* @property-read Horde_ActiveSync_Wbxml_Encoder $encoder The Wbxml encoder.
|
|
* @property-read Horde_ActiveSync_Wbxml_Decoder $decoder The Wbxml decoder.
|
|
* @property-read Horde_ActiveSync_State_Base $state The state object.
|
|
* @property-read Horde_Controller_Reqeust_Http $request The HTTP request object.
|
|
* @property-read Horde_ActiveSync_Driver_Base $driver The backend driver object.
|
|
* @property-read boolean|string $provisioning Provisioning support: True, False, or 'loose'
|
|
* @property-read boolean $multipart Indicate this is a multipart request.
|
|
* @property-read string $certPath Local path to the certificate bundle.
|
|
* @property-read Horde_ActiveSync_Device $device The current device object.
|
|
* @property-read Horde_ActiveSync_Log_Logger $logger The logger object.
|
|
*/
|
|
class Horde_ActiveSync
|
|
{
|
|
/* Conflict resolution */
|
|
const CONFLICT_OVERWRITE_SERVER = 0;
|
|
const CONFLICT_OVERWRITE_PIM = 1;
|
|
|
|
/* TRUNCATION Constants */
|
|
const TRUNCATION_ALL = 0;
|
|
const TRUNCATION_1 = 1;
|
|
const TRUNCATION_2 = 2;
|
|
const TRUNCATION_3 = 3;
|
|
const TRUNCATION_4 = 4;
|
|
const TRUNCATION_5 = 5;
|
|
const TRUNCATION_6 = 6;
|
|
const TRUNCATION_7 = 7;
|
|
const TRUNCATION_8 = 8;
|
|
const TRUNCATION_9 = 9;
|
|
const TRUNCATION_NONE = 9; // @deprecated
|
|
|
|
/* FOLDERHIERARCHY */
|
|
const FOLDERHIERARCHY_FOLDERS = 'FolderHierarchy:Folders';
|
|
const FOLDERHIERARCHY_FOLDER = 'FolderHierarchy:Folder';
|
|
const FOLDERHIERARCHY_DISPLAYNAME = 'FolderHierarchy:DisplayName';
|
|
const FOLDERHIERARCHY_SERVERENTRYID = 'FolderHierarchy:ServerEntryId';
|
|
const FOLDERHIERARCHY_PARENTID = 'FolderHierarchy:ParentId';
|
|
const FOLDERHIERARCHY_TYPE = 'FolderHierarchy:Type';
|
|
const FOLDERHIERARCHY_RESPONSE = 'FolderHierarchy:Response';
|
|
const FOLDERHIERARCHY_STATUS = 'FolderHierarchy:Status';
|
|
const FOLDERHIERARCHY_CONTENTCLASS = 'FolderHierarchy:ContentClass';
|
|
const FOLDERHIERARCHY_CHANGES = 'FolderHierarchy:Changes';
|
|
const FOLDERHIERARCHY_SYNCKEY = 'FolderHierarchy:SyncKey';
|
|
const FOLDERHIERARCHY_FOLDERSYNC = 'FolderHierarchy:FolderSync';
|
|
const FOLDERHIERARCHY_COUNT = 'FolderHierarchy:Count';
|
|
const FOLDERHIERARCHY_VERSION = 'FolderHierarchy:Version';
|
|
|
|
/* SYNC */
|
|
const SYNC_SYNCHRONIZE = 'Synchronize';
|
|
const SYNC_REPLIES = 'Replies';
|
|
const SYNC_ADD = 'Add';
|
|
const SYNC_MODIFY = 'Modify';
|
|
const SYNC_REMOVE = 'Remove';
|
|
const SYNC_FETCH = 'Fetch';
|
|
const SYNC_SYNCKEY = 'SyncKey';
|
|
const SYNC_CLIENTENTRYID = 'ClientEntryId';
|
|
const SYNC_SERVERENTRYID = 'ServerEntryId';
|
|
const SYNC_STATUS = 'Status';
|
|
const SYNC_FOLDER = 'Folder';
|
|
const SYNC_FOLDERTYPE = 'FolderType';
|
|
const SYNC_VERSION = 'Version';
|
|
const SYNC_FOLDERID = 'FolderId';
|
|
const SYNC_GETCHANGES = 'GetChanges';
|
|
const SYNC_MOREAVAILABLE = 'MoreAvailable';
|
|
const SYNC_WINDOWSIZE = 'WindowSize';
|
|
const SYNC_COMMANDS = 'Commands';
|
|
const SYNC_OPTIONS = 'Options';
|
|
const SYNC_FILTERTYPE = 'FilterType';
|
|
const SYNC_TRUNCATION = 'Truncation';
|
|
const SYNC_RTFTRUNCATION = 'RtfTruncation';
|
|
const SYNC_CONFLICT = 'Conflict';
|
|
const SYNC_FOLDERS = 'Folders';
|
|
const SYNC_DATA = 'Data';
|
|
const SYNC_DELETESASMOVES = 'DeletesAsMoves';
|
|
const SYNC_NOTIFYGUID = 'NotifyGUID';
|
|
const SYNC_SUPPORTED = 'Supported';
|
|
const SYNC_SOFTDELETE = 'SoftDelete';
|
|
const SYNC_MIMESUPPORT = 'MIMESupport';
|
|
const SYNC_MIMETRUNCATION = 'MIMETruncation';
|
|
const SYNC_NEWMESSAGE = 'NewMessage';
|
|
const SYNC_PARTIAL = 'Partial';
|
|
const SYNC_WAIT = 'Wait';
|
|
const SYNC_LIMIT = 'Limit';
|
|
// 14
|
|
const SYNC_HEARTBEATINTERVAL = 'HeartbeatInterval';
|
|
const SYNC_CONVERSATIONMODE = 'ConversationMode';
|
|
const SYNC_MAXITEMS = 'MaxItems';
|
|
|
|
/* Document library */
|
|
const SYNC_DOCUMENTLIBRARY_LINKID = 'DocumentLibrary:LinkId';
|
|
const SYNC_DOCUMENTLIBRARY_DISPLAYNAME = 'DocumentLibrary:DisplayName';
|
|
const SYNC_DOCUMENTLIBRARY_ISFOLDER = 'DocumentLibrary:IsFolder';
|
|
const SYNC_DOCUMENTLIBRARY_CREATIONDATE = 'DocumentLibrary:CreationDate';
|
|
const SYNC_DOCUMENTLIBRARY_LASTMODIFIEDDATE = 'DocumentLibrary:LastModifiedDate';
|
|
const SYNC_DOCUMENTLIBRARY_ISHIDDEN = 'DocumentLibrary:IsHidden';
|
|
const SYNC_DOCUMENTLIBRARY_CONTENTLENGTH = 'DocumentLibrary:ContentLength';
|
|
const SYNC_DOCUMENTLIBRARY_CONTENTTYPE = 'DocumentLibrary:ContentType';
|
|
|
|
/* AIRSYNCBASE */
|
|
const AIRSYNCBASE_BODYPREFERENCE = 'AirSyncBase:BodyPreference';
|
|
const AIRSYNCBASE_TYPE = 'AirSyncBase:Type';
|
|
const AIRSYNCBASE_TRUNCATIONSIZE = 'AirSyncBase:TruncationSize';
|
|
const AIRSYNCBASE_ALLORNONE = 'AirSyncBase:AllOrNone';
|
|
const AIRSYNCBASE_BODY = 'AirSyncBase:Body';
|
|
const AIRSYNCBASE_DATA = 'AirSyncBase:Data';
|
|
const AIRSYNCBASE_ESTIMATEDDATASIZE = 'AirSyncBase:EstimatedDataSize';
|
|
const AIRSYNCBASE_TRUNCATED = 'AirSyncBase:Truncated';
|
|
const AIRSYNCBASE_ATTACHMENTS = 'AirSyncBase:Attachments';
|
|
const AIRSYNCBASE_ATTACHMENT = 'AirSyncBase:Attachment';
|
|
const AIRSYNCBASE_DISPLAYNAME = 'AirSyncBase:DisplayName';
|
|
const AIRSYNCBASE_FILEREFERENCE = 'AirSyncBase:FileReference';
|
|
const AIRSYNCBASE_METHOD = 'AirSyncBase:Method';
|
|
const AIRSYNCBASE_CONTENTID = 'AirSyncBase:ContentId';
|
|
const AIRSYNCBASE_CONTENTLOCATION = 'AirSyncBase:ContentLocation';
|
|
const AIRSYNCBASE_ISINLINE = 'AirSyncBase:IsInline';
|
|
const AIRSYNCBASE_NATIVEBODYTYPE = 'AirSyncBase:NativeBodyType';
|
|
const AIRSYNCBASE_CONTENTTYPE = 'AirSyncBase:ContentType';
|
|
const AIRSYNCBASE_LOCATION = 'AirSyncBase:Location';
|
|
|
|
// 14.0
|
|
const AIRSYNCBASE_PREVIEW = 'AirSyncBase:Preview';
|
|
|
|
// 14.1
|
|
const AIRSYNCBASE_BODYPARTPREFERENCE = 'AirSyncBase:BodyPartPreference';
|
|
const AIRSYNCBASE_BODYPART = 'AirSyncBase:BodyPart';
|
|
const AIRSYNCBASE_STATUS = 'AirSyncBase:Status';
|
|
|
|
// 16.0
|
|
const AIRSYNCBASE_ADD = 'AirSyncBase:Add';
|
|
const AIRSYNCBASE_DELETE = 'AirSyncBase:Delete';
|
|
const AIRSYNCBASE_CLIENTID = 'AirSyncBase:ClientId';
|
|
const AIRSYNCBASE_CONTENT = 'AirSyncBase:Content';
|
|
const AIRSYNCBASE_ANNOTATION = 'AirSyncBase:Annotation';
|
|
const AIRSYNCBASE_STREET = 'AirSyncBase:Street';
|
|
const AIRSYNCBASE_CITY = 'AirSyncBase:City';
|
|
const AIRSYNCBASE_STATE = 'AirSyncBase:State';
|
|
const AIRSYNCBASE_COUNTRY = 'AirSyncBase:Country';
|
|
const AIRSYNCBASE_POSTALCODE = 'AirSyncBase:PostalCode';
|
|
const AIRSYNCBASE_LATITUDE = 'AirSyncBase:Latitude';
|
|
const AIRSYNCBASE_LONGITUDE = 'AirSyncBase:Longitude';
|
|
const AIRSYNCBASE_ACCURACY = 'AirSyncBase:Accuracy';
|
|
const AIRSYNCBASE_ALTITUDE = 'AirSyncBase:Altitude';
|
|
const AIRSYNCBASE_ALTITUDEACCURACY = 'AirSyncBase:AltitudeAccuracy';
|
|
const AIRSYNCBASE_LOCATIONURI = 'AirSyncBase:LocationUri';
|
|
const AIRSYNCBASE_INSTANCEID = 'AirSyncBase:InstanceId';
|
|
|
|
|
|
/* Body type prefs */
|
|
const BODYPREF_TYPE_PLAIN = 1;
|
|
const BODYPREF_TYPE_HTML = 2;
|
|
const BODYPREF_TYPE_RTF = 3;
|
|
const BODYPREF_TYPE_MIME = 4;
|
|
|
|
/* PROVISION */
|
|
const PROVISION_PROVISION = 'Provision:Provision';
|
|
const PROVISION_POLICIES = 'Provision:Policies';
|
|
const PROVISION_POLICY = 'Provision:Policy';
|
|
const PROVISION_POLICYTYPE = 'Provision:PolicyType';
|
|
const PROVISION_POLICYKEY = 'Provision:PolicyKey';
|
|
const PROVISION_DATA = 'Provision:Data';
|
|
const PROVISION_STATUS = 'Provision:Status';
|
|
const PROVISION_REMOTEWIPE = 'Provision:RemoteWipe';
|
|
const PROVISION_EASPROVISIONDOC = 'Provision:EASProvisionDoc';
|
|
|
|
/* Policy types */
|
|
const POLICYTYPE_XML = 'MS-WAP-Provisioning-XML';
|
|
const POLICYTYPE_WBXML = 'MS-EAS-Provisioning-WBXML';
|
|
|
|
/* Flags */
|
|
// @TODO: H6 Change this to CHANGE_TYPE_NEW
|
|
const FLAG_NEWMESSAGE = 'NewMessage';
|
|
|
|
/* Folder types */
|
|
const FOLDER_TYPE_OTHER = 1;
|
|
const FOLDER_TYPE_INBOX = 2;
|
|
const FOLDER_TYPE_DRAFTS = 3;
|
|
const FOLDER_TYPE_WASTEBASKET = 4;
|
|
const FOLDER_TYPE_SENTMAIL = 5;
|
|
const FOLDER_TYPE_OUTBOX = 6;
|
|
const FOLDER_TYPE_TASK = 7;
|
|
const FOLDER_TYPE_APPOINTMENT = 8;
|
|
const FOLDER_TYPE_CONTACT = 9;
|
|
const FOLDER_TYPE_NOTE = 10;
|
|
const FOLDER_TYPE_JOURNAL = 11;
|
|
const FOLDER_TYPE_USER_MAIL = 12;
|
|
const FOLDER_TYPE_USER_APPOINTMENT = 13;
|
|
const FOLDER_TYPE_USER_CONTACT = 14;
|
|
const FOLDER_TYPE_USER_TASK = 15;
|
|
const FOLDER_TYPE_USER_JOURNAL = 16;
|
|
const FOLDER_TYPE_USER_NOTE = 17;
|
|
const FOLDER_TYPE_UNKNOWN = 18;
|
|
const FOLDER_TYPE_RECIPIENT_CACHE = 19;
|
|
// @TODO, remove const definition in H6, not used anymore.
|
|
const FOLDER_TYPE_DUMMY = 999999;
|
|
|
|
/* Origin of changes **/
|
|
const CHANGE_ORIGIN_PIM = 0;
|
|
const CHANGE_ORIGIN_SERVER = 1;
|
|
const CHANGE_ORIGIN_NA = 3;
|
|
|
|
/* Remote wipe **/
|
|
const RWSTATUS_NA = 0;
|
|
const RWSTATUS_OK = 1;
|
|
const RWSTATUS_PENDING = 2;
|
|
const RWSTATUS_WIPED = 3;
|
|
|
|
/* GAL **/
|
|
const GAL_DISPLAYNAME = 'GAL:DisplayName';
|
|
const GAL_PHONE = 'GAL:Phone';
|
|
const GAL_OFFICE = 'GAL:Office';
|
|
const GAL_TITLE = 'GAL:Title';
|
|
const GAL_COMPANY = 'GAL:Company';
|
|
const GAL_ALIAS = 'GAL:Alias';
|
|
const GAL_FIRSTNAME = 'GAL:FirstName';
|
|
const GAL_LASTNAME = 'GAL:LastName';
|
|
const GAL_HOMEPHONE = 'GAL:HomePhone';
|
|
const GAL_MOBILEPHONE = 'GAL:MobilePhone';
|
|
const GAL_EMAILADDRESS = 'GAL:EmailAddress';
|
|
// 14.1
|
|
const GAL_PICTURE = 'GAL:Picture';
|
|
const GAL_STATUS = 'GAL:Status';
|
|
const GAL_DATA = 'GAL:Data';
|
|
|
|
/* Request Type */
|
|
const REQUEST_TYPE_SYNC = 'sync';
|
|
const REQUEST_TYPE_FOLDERSYNC = 'foldersync';
|
|
|
|
/* Change Type */
|
|
const CHANGE_TYPE_CHANGE = 'change';
|
|
const CHANGE_TYPE_DELETE = 'delete';
|
|
const CHANGE_TYPE_FLAGS = 'flags';
|
|
const CHANGE_TYPE_MOVE = 'move';
|
|
const CHANGE_TYPE_FOLDERSYNC = 'foldersync';
|
|
const CHANGE_TYPE_SOFTDELETE = 'softdelete';
|
|
|
|
// @since 2.36.0
|
|
const CHANGE_TYPE_DRAFT = 'draft';
|
|
|
|
/* Internal flags to indicate change is a change in reply/forward state */
|
|
const CHANGE_REPLY_STATE = '@--reply--@';
|
|
const CHANGE_REPLYALL_STATE = '@--replyall--@';
|
|
const CHANGE_FORWARD_STATE = '@--forward--@';
|
|
|
|
/* RM */
|
|
const RM_SUPPORT = 'RightsManagement:RightsManagementSupport';
|
|
const RM_TEMPLATEID = 'RightsManagement:TemplateId';
|
|
|
|
/* Collection Classes */
|
|
const CLASS_EMAIL = 'Email';
|
|
const CLASS_CONTACTS = 'Contacts';
|
|
const CLASS_CALENDAR = 'Calendar';
|
|
const CLASS_TASKS = 'Tasks';
|
|
const CLASS_NOTES = 'Notes';
|
|
const CLASS_SMS = 'SMS';
|
|
|
|
/* Filtertype constants */
|
|
const FILTERTYPE_ALL = 0;
|
|
const FILTERTYPE_1DAY = 1;
|
|
const FILTERTYPE_3DAYS = 2;
|
|
const FILTERTYPE_1WEEK = 3;
|
|
const FILTERTYPE_2WEEKS = 4;
|
|
const FILTERTYPE_1MONTH = 5;
|
|
const FILTERTYPE_3MONTHS = 6;
|
|
const FILTERTYPE_6MONTHS = 7;
|
|
const FILTERTYPE_INCOMPLETETASKS = 8;
|
|
|
|
// @todo normalize to string values.
|
|
const PROVISIONING_FORCE = true;
|
|
const PROVISIONING_LOOSE = 'loose';
|
|
const PROVISIONING_NONE = false;
|
|
|
|
const FOLDER_ROOT = 0;
|
|
|
|
const VERSION_TWOFIVE = '2.5';
|
|
const VERSION_TWELVE = '12.0';
|
|
const VERSION_TWELVEONE = '12.1';
|
|
const VERSION_FOURTEEN = '14.0';
|
|
const VERSION_FOURTEENONE = '14.1';
|
|
const VERSION_SIXTEEN = '16.0';
|
|
|
|
const MIME_SUPPORT_NONE = 0;
|
|
const MIME_SUPPORT_SMIME = 1;
|
|
const MIME_SUPPORT_ALL = 2;
|
|
|
|
const IMAP_FLAG_REPLY = 'reply';
|
|
const IMAP_FLAG_FORWARD = 'forward';
|
|
|
|
/* Result Type */
|
|
const RESOLVE_RESULT_GAL = 1;
|
|
const RESOLVE_RESULT_ADDRESSBOOK = 2;
|
|
|
|
/* Auth failure reasons */
|
|
const AUTH_REASON_USER_DENIED = 'user';
|
|
const AUTH_REASON_DEVICE_DENIED = 'device';
|
|
|
|
/* Internal flag indicates all possible fields are ghosted */
|
|
const ALL_GHOSTED = 'allghosted';
|
|
|
|
const LIBRARY_VERSION = '2.41.5';
|
|
|
|
/**
|
|
* Logger
|
|
*
|
|
* @var Horde_ActiveSync_Interface_LoggerFactory
|
|
*/
|
|
protected $_loggerFactory;
|
|
|
|
/**
|
|
* The logger for this class.
|
|
*
|
|
* @var Horde_Log_Logger
|
|
*/
|
|
protected static $_logger;
|
|
|
|
/**
|
|
* Provisioning support
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_provisioning;
|
|
|
|
/**
|
|
* Highest version to support.
|
|
*
|
|
* @var float
|
|
*/
|
|
protected $_maxVersion = self::VERSION_SIXTEEN;
|
|
|
|
/**
|
|
* The actual version we are supporting.
|
|
*
|
|
* @var float
|
|
*/
|
|
protected static $_version;
|
|
|
|
/**
|
|
* Multipart support?
|
|
*
|
|
* @var boolean
|
|
*/
|
|
protected $_multipart = false;
|
|
|
|
/**
|
|
* Support gzip compression of certain data parts?
|
|
*
|
|
* @var boolean
|
|
*/
|
|
protected $_compression = false;
|
|
|
|
/**
|
|
* Local cache of Get variables/decoded base64 uri
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_get = array();
|
|
|
|
/**
|
|
* Path to root certificate bundle
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_certPath;
|
|
|
|
/**
|
|
*
|
|
* @var Horde_ActiveSync_Device
|
|
*/
|
|
protected static $_device;
|
|
|
|
/**
|
|
* Wbxml encoder
|
|
*
|
|
* @var Horde_ActiveSync_Wbxml_Encoder
|
|
*/
|
|
protected $_encoder;
|
|
|
|
/**
|
|
* Wbxml decoder
|
|
*
|
|
* @var Horde_ActiveSync_Wbxml_Decoder
|
|
*/
|
|
protected $_decoder;
|
|
|
|
/**
|
|
* The singleton collections handler.
|
|
*
|
|
* @var Horde_ActiveSync_Collections
|
|
*/
|
|
protected $_collectionsObj;
|
|
|
|
/**
|
|
* Global error flag.
|
|
*
|
|
* @var boolean
|
|
*/
|
|
protected $_globalError = false;
|
|
|
|
/**
|
|
* Process id (used in logging).
|
|
*
|
|
* @var integer
|
|
*/
|
|
protected $_procid;
|
|
|
|
/**
|
|
* Flag to indicate we need to update the device version.
|
|
*
|
|
* @var boolean
|
|
*/
|
|
protected $_needMsRp = false;
|
|
|
|
/**
|
|
* Supported EAS versions.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected static $_supportedVersions = array(
|
|
self::VERSION_TWOFIVE,
|
|
self::VERSION_TWELVE,
|
|
self::VERSION_TWELVEONE,
|
|
self::VERSION_FOURTEEN,
|
|
self::VERSION_FOURTEENONE,
|
|
self::VERSION_SIXTEEN
|
|
);
|
|
|
|
/**
|
|
* Factory method for creating Horde_ActiveSync_Message objects.
|
|
*
|
|
* @param string $message The message type.
|
|
* @since 2.4.0
|
|
*
|
|
* @return Horde_ActiveSync_Message_Base The concrete message object.
|
|
* @todo For H6, move to Horde_ActiveSync_Message_Base::factory()
|
|
*/
|
|
public static function messageFactory($message)
|
|
{
|
|
$class = 'Horde_ActiveSync_Message_' . $message;
|
|
if (!class_exists($class)) {
|
|
throw new InvalidArgumentException(sprintf('Class %s does not exist.', $class));
|
|
}
|
|
|
|
return new $class(array(
|
|
'logger' => self::$_logger,
|
|
'protocolversion' => self::$_version,
|
|
'device' => self::$_device));
|
|
}
|
|
|
|
/**
|
|
* Const'r
|
|
*
|
|
* @param Horde_ActiveSync_Driver_Base $driver The backend driver.
|
|
* @param Horde_ActiveSync_Wbxml_Decoder $decoder The Wbxml decoder.
|
|
* @param Horde_ActiveSync_Wbxml_Endcoder $encoder The Wbxml encoder.
|
|
* @param Horde_ActiveSync_State_Base $state The state driver.
|
|
* @param Horde_Controller_Request_Http $request The HTTP request object.
|
|
*
|
|
* @return Horde_ActiveSync The ActiveSync server object.
|
|
*/
|
|
public function __construct(
|
|
Horde_ActiveSync_Driver_Base $driver,
|
|
Horde_ActiveSync_Wbxml_Decoder $decoder,
|
|
Horde_ActiveSync_Wbxml_Encoder $encoder,
|
|
Horde_ActiveSync_State_Base $state,
|
|
Horde_Controller_Request_Http $request)
|
|
{
|
|
// The http request
|
|
$this->_request = $request;
|
|
|
|
// Backend driver
|
|
$this->_driver = $driver;
|
|
$this->_driver->setProtocolVersion($this->getProtocolVersion());
|
|
|
|
// Device state manager
|
|
$this->_state = $state;
|
|
|
|
// Wbxml handlers
|
|
$this->_encoder = $encoder;
|
|
$this->_decoder = $decoder;
|
|
|
|
$this->_procid = getmypid();
|
|
}
|
|
|
|
/**
|
|
* Return a collections singleton.
|
|
*
|
|
* @return Horde_ActiveSync_Collections
|
|
* @since 2.4.0
|
|
*/
|
|
public function getCollectionsObject()
|
|
{
|
|
if (empty($this->_collectionsObj)) {
|
|
$this->_collectionsObj = new Horde_ActiveSync_Collections($this->getSyncCache(), $this);
|
|
}
|
|
|
|
return $this->_collectionsObj;
|
|
}
|
|
|
|
/**
|
|
* Return a new, fully configured SyncCache.
|
|
*
|
|
* @return Horde_ActiveSync_SyncCache
|
|
* @since 2.4.0
|
|
*/
|
|
public function getSyncCache()
|
|
{
|
|
return new Horde_ActiveSync_SyncCache(
|
|
$this->_state,
|
|
self::$_device->id,
|
|
self::$_device->user,
|
|
self::$_logger
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Return an Importer object.
|
|
*
|
|
* @return Horde_ActiveSync_Connector_Importer
|
|
* @since 2.4.0
|
|
*/
|
|
public function getImporter()
|
|
{
|
|
$importer = new Horde_ActiveSync_Connector_Importer($this);
|
|
$importer->setLogger(self::$_logger);
|
|
|
|
return $importer;
|
|
}
|
|
|
|
/**
|
|
* Authenticate to the backend.
|
|
*
|
|
* @param Horde_ActiveSync_Credentials $credentials The credentials object.
|
|
*
|
|
* @return boolean True on successful authentication to the backend.
|
|
* @throws Horde_ActiveSync_Exception
|
|
*/
|
|
public function authenticate(Horde_ActiveSync_Credentials $credentials)
|
|
{
|
|
if (!$credentials->username) {
|
|
// No provided username or Authorization header.
|
|
self::$_logger->notice('Client did not provide authentication data.');
|
|
return false;
|
|
}
|
|
|
|
$user = $this->_driver->getUsernameFromEmail($credentials->username);
|
|
$pos = strrpos($user, '\\');
|
|
if ($pos !== false) {
|
|
$domain = substr($user, 0, $pos);
|
|
$user = substr($user, $pos + 1);
|
|
} else {
|
|
$domain = null;
|
|
}
|
|
|
|
// Authenticate
|
|
if ($result = $this->_driver->authenticate($user, $credentials->password, $domain)) {
|
|
if ($result === self::AUTH_REASON_USER_DENIED) {
|
|
$this->_globalError = Horde_ActiveSync_Status::SYNC_NOT_ALLOWED;
|
|
} elseif ($result === self::AUTH_REASON_DEVICE_DENIED) {
|
|
$this->_globalError = Horde_ActiveSync_Status::DEVICE_BLOCKED_FOR_USER;
|
|
} elseif ($result !== true) {
|
|
$this->_globalError = Horde_ActiveSync_Status::DENIED;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
if (!$this->_driver->setup($user)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Allow to force the highest version to support.
|
|
*
|
|
* @param float $version The highest version
|
|
*/
|
|
public function setSupportedVersion($version)
|
|
{
|
|
$this->_maxVersion = $version;
|
|
}
|
|
|
|
/**
|
|
* Set the local path to the root certificate bundle.
|
|
*
|
|
* @param string $path The local path to the bundle.
|
|
*/
|
|
public function setRootCertificatePath($path)
|
|
{
|
|
$this->_certPath = $path;
|
|
}
|
|
|
|
/**
|
|
* Getter
|
|
*
|
|
* @param string $property The property to return.
|
|
*
|
|
* @return mixed The value of the requested property.
|
|
*/
|
|
public function __get($property)
|
|
{
|
|
switch ($property) {
|
|
case 'encoder':
|
|
case 'decoder':
|
|
case 'state':
|
|
case 'request':
|
|
case 'driver':
|
|
case 'provisioning':
|
|
case 'multipart':
|
|
case 'certPath':
|
|
$property = '_' . $property;
|
|
return $this->$property;
|
|
case 'logger':
|
|
return self::$_logger;
|
|
case 'device':
|
|
return self::$_device;
|
|
default:
|
|
throw new InvalidArgumentException(sprintf(
|
|
'The property %s does not exist',
|
|
$property)
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Setter for the logger factory.
|
|
*
|
|
* @param Horde_ActiveSync_Interface_LoggerFactory $logger The logger factory.
|
|
*/
|
|
public function setLogger(Horde_ActiveSync_Interface_LoggerFactory $logger)
|
|
{
|
|
$this->_loggerFactory = $logger;
|
|
}
|
|
|
|
/**
|
|
* Instantiate the logger from the factory and inject into all needed
|
|
* objects.
|
|
*
|
|
* @param array $options [description]
|
|
*/
|
|
protected function _setLogger(array $options)
|
|
{
|
|
if (!empty($this->_loggerFactory)) {
|
|
// @TODO. Remove wrapper.
|
|
self::$_logger = self::_wrapLogger($this->_loggerFactory->create($options));
|
|
$this->_encoder->setLogger(self::$_logger);
|
|
$this->_decoder->setLogger(self::$_logger);
|
|
$this->_driver->setLogger(self::$_logger);
|
|
$this->_state->setLogger(self::$_logger);
|
|
}
|
|
}
|
|
|
|
public static function _wrapLogger(Horde_Log_Logger $logger)
|
|
{
|
|
if (!($logger instanceof Horde_ActiveSync_Log_Logger)) {
|
|
return new Horde_ActiveSync_Log_Logger_Deprecated(null, $logger);
|
|
}
|
|
|
|
return $logger;
|
|
}
|
|
|
|
/**
|
|
* Setter for provisioning support
|
|
*
|
|
*/
|
|
public function setProvisioning($provision)
|
|
{
|
|
$this->_provisioning = $provision;
|
|
}
|
|
|
|
/**
|
|
* Send the headers indicating that provisioning is required.
|
|
*/
|
|
public function provisioningRequired()
|
|
{
|
|
$this->provisionHeader();
|
|
$this->activeSyncHeader();
|
|
$this->versionHeader();
|
|
$this->commandsHeader();
|
|
header('Cache-Control: private');
|
|
}
|
|
|
|
/**
|
|
* The heart of the server. Dispatch a request to the appropriate request
|
|
* handler.
|
|
*
|
|
* @param string $cmd The command we are requesting.
|
|
* @param string $devId The device id making the request. @deprecated
|
|
*
|
|
* @return string|boolean false if failed, true if succeeded and response
|
|
* content is wbxml, otherwise the
|
|
* content-type string to send in the response.
|
|
* @throws Horde_ActiveSync_Exception
|
|
* @throws Horde_ActiveSync_Exception_InvalidRequest
|
|
* @throws Horde_ActiveSync_PermissionDenied
|
|
*/
|
|
public function handleRequest($cmd, $devId)
|
|
{
|
|
$get = $this->getGetVars();
|
|
if (empty($cmd)) {
|
|
$cmd = $get['Cmd'];
|
|
}
|
|
if (empty($devId)) {
|
|
$devId = !empty($get['DeviceId']) ? Horde_String::upper($get['DeviceId']) : null;
|
|
} else {
|
|
$devId = Horde_String::upper($devId);
|
|
}
|
|
$this->_setLogger($get);
|
|
|
|
// @TODO: Remove is_callable check for H6.
|
|
// Callback to give the backend the option to limit EAS version based
|
|
// on user/device/etc...
|
|
if (is_callable(array($this->_driver, 'versionCallback'))) {
|
|
$this->_driver->versionCallback($this);
|
|
}
|
|
|
|
// Autodiscovery handles authentication on it's own.
|
|
if ($cmd == 'Autodiscover') {
|
|
$request = new Horde_ActiveSync_Request_Autodiscover($this, new Horde_ActiveSync_Device($this->_state));
|
|
|
|
if (!empty(self::$_logger)) {
|
|
$request->setLogger(self::$_logger);
|
|
}
|
|
|
|
$result = $request->handle($this->_request);
|
|
$this->_driver->clearAuthentication();
|
|
return $result;
|
|
}
|
|
if (!$this->authenticate(new Horde_ActiveSync_Credentials($this))) {
|
|
$this->activeSyncHeader();
|
|
$this->versionHeader();
|
|
$this->commandsHeader();
|
|
throw new Horde_Exception_AuthenticationFailure();
|
|
}
|
|
|
|
self::$_logger->info(sprintf(
|
|
'%s%s request received for user %s',
|
|
str_repeat('-', 10),
|
|
Horde_String::upper($cmd),
|
|
$this->_driver->getUser())
|
|
);
|
|
|
|
// These are all handled in the same class.
|
|
if ($cmd == 'FolderDelete' || $cmd == 'FolderUpdate') {
|
|
$cmd = 'FolderCreate';
|
|
}
|
|
|
|
// Device id is REQUIRED
|
|
if (empty($devId)) {
|
|
if ($cmd == 'Options') {
|
|
$this->_handleOptionsRequest();
|
|
$this->_driver->clearAuthentication();
|
|
return true;
|
|
}
|
|
$this->_driver->clearAuthentication();
|
|
throw new Horde_ActiveSync_Exception_InvalidRequest('Device failed to send device id.');
|
|
}
|
|
|
|
// EAS Version
|
|
$version = $this->getProtocolVersion();
|
|
|
|
// Device. Even though versions of EAS > 12.1 are supposed to send
|
|
// EAS status codes back to indicate various errors in allowing a client
|
|
// to connect, we just throw an exception (thus causing a HTTP error
|
|
// code to be sent as in versions 12.1 and below). Until we refactor for
|
|
// Horde 6, we don't know the response type to wrap the status code in
|
|
// until we load the request handler, which requires we start to parse
|
|
// the WBXML stream and device information etc... This saves resources
|
|
// as well as keeps things cleaner until we refactor.
|
|
$device_result = $this->_handleDevice($devId);
|
|
|
|
// Don't bother with everything else if all we want are Options
|
|
if ($cmd == 'Options') {
|
|
$this->_handleOptionsRequest();
|
|
$this->_driver->clearAuthentication();
|
|
return true;
|
|
}
|
|
|
|
// Set provisioning support now that we are authenticated.
|
|
$this->setProvisioning($this->_driver->getProvisioning(self::$_device));
|
|
|
|
// Read the initial Wbxml header
|
|
$this->_decoder->readWbxmlHeader();
|
|
|
|
// Support Multipart response for ITEMOPERATIONS requests?
|
|
$headers = $this->_request->getHeaders();
|
|
if ((!empty($headers['ms-asacceptmultipart']) && $headers['ms-asacceptmultipart'] == 'T') ||
|
|
!empty($get['AcceptMultiPart'])) {
|
|
$this->_multipart = true;
|
|
self::$_logger->info('Requesting multipart data.');
|
|
}
|
|
|
|
// Load the request handler to handle the request
|
|
// We must send the EAS header here, since some requests may start
|
|
// output and be large enough to flush the buffer (e.g., GetAttachment)
|
|
// See Bug: 12486
|
|
$this->activeSyncHeader();
|
|
if ($cmd != 'GetAttachment') {
|
|
$this->contentTypeHeader();
|
|
}
|
|
|
|
// Should we announce a new version is available to the client?
|
|
if (!empty($this->_needMsRp)) {
|
|
self::$_logger->info('Announcing X-MS-RP to client.');
|
|
header("X-MS-RP: ". $this->getSupportedVersions());
|
|
}
|
|
|
|
// @TODO: Look at getting rid of having to set the version in the driver
|
|
// and get it from the device object for H6.
|
|
$this->_driver->setDevice(self::$_device);
|
|
$class = 'Horde_ActiveSync_Request_' . basename($cmd);
|
|
if (class_exists($class)) {
|
|
$request = new $class($this);
|
|
$request->setLogger(self::$_logger);
|
|
$result = $request->handle();
|
|
self::$_logger->info(sprintf(
|
|
'Maximum memory usage for ActiveSync request: %d bytes.',
|
|
memory_get_peak_usage(true))
|
|
);
|
|
|
|
return $result;
|
|
}
|
|
|
|
$this->_driver->clearAuthentication();
|
|
throw new Horde_ActiveSync_Exception_InvalidRequest(basename($cmd) . ' not supported.');
|
|
}
|
|
|
|
/**
|
|
* Handle device checks. Takes into account permissions and restrictions
|
|
* via various callback methods.
|
|
*
|
|
* @param string $devId The client provided device id.
|
|
*
|
|
* @return boolean If EAS version is > 12.1 returns false on any type of
|
|
* failure in allowing the device to connect. Sets
|
|
* appropriate internal variables to indicate the type of
|
|
* error to return to the client. Failure on EAS version
|
|
* < 12.1 results in throwing exceptions. Otherwise, return
|
|
* true.
|
|
* @throws Horde_ActiveSync_Exception, Horde_Exception_AuthenticationFailure
|
|
*/
|
|
protected function _handleDevice($devId)
|
|
{
|
|
$get = $this->getGetVars();
|
|
$version = $this->getProtocolVersion();
|
|
|
|
// Does device exist AND does the user have an account on the device?
|
|
if (!$this->_state->deviceExists($devId, $this->_driver->getUser())) {
|
|
// Device might exist, but with a new (additional) user account
|
|
if ($this->_state->deviceExists($devId)) {
|
|
self::$_device = $this->_state->loadDeviceInfo($devId);
|
|
} else {
|
|
self::$_device = new Horde_ActiveSync_Device($this->_state);
|
|
}
|
|
self::$_device->policykey = 0;
|
|
self::$_device->userAgent = $this->_request->getHeader('User-Agent');
|
|
self::$_device->deviceType = !empty($get['DeviceType']) ? $get['DeviceType'] : '';
|
|
self::$_device->rwstatus = self::RWSTATUS_NA;
|
|
self::$_device->user = $this->_driver->getUser();
|
|
self::$_device->id = $devId;
|
|
self::$_device->needsVersionUpdate($this->getSupportedVersions());
|
|
self::$_device->version = $version;
|
|
// @TODO: Remove is_callable check for H6.
|
|
// Combine this with the modifyDevice callback? Allow $device
|
|
// to be modified here?
|
|
if (is_callable(array($this->_driver, 'createDeviceCallback'))) {
|
|
$callback_ret = $this->_driver->createDeviceCallback(self::$_device);
|
|
if ($callback_ret !== true) {
|
|
$msg = sprintf(
|
|
'The device %s was disallowed for user %s per policy settings.',
|
|
self::$_device->id,
|
|
self::$_device->user);
|
|
self::$_logger->err($msg);
|
|
// Always throw exception in place of status code since we
|
|
// won't have a version number before the device is created.
|
|
throw new Horde_Exception_AuthenticationFailure($msg, $callback_ret);
|
|
} else {
|
|
// Give the driver a chance to modify device properties.
|
|
if (is_callable(array($this->_driver, 'modifyDeviceCallback'))) {
|
|
self::$_device = $this->_driver->modifyDeviceCallback(self::$_device);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
self::$_device = $this->_state->loadDeviceInfo($devId, $this->_driver->getUser());
|
|
|
|
// If the device state was removed from storage, we may lose the
|
|
// device properties, so try to repopulate what we can. userAgent
|
|
// is ALWAYS available, so if it's missing, the state is gone.
|
|
if (empty(self::$_device->userAgent)) {
|
|
self::$_device->userAgent = $this->_request->getHeader('User-Agent');
|
|
self::$_device->deviceType = !empty($get['DeviceType']) ? $get['DeviceType'] : '';
|
|
self::$_device->user = $this->_driver->getUser();
|
|
}
|
|
|
|
if (empty(self::$_device->version)) {
|
|
self::$_device->version = $version;
|
|
}
|
|
if (self::$_device->version < $this->_maxVersion &&
|
|
self::$_device->needsVersionUpdate($this->getSupportedVersions())) {
|
|
$this->_needMsRp = true;
|
|
}
|
|
|
|
// Give the driver a chance to modify device properties.
|
|
if (is_callable(array($this->_driver, 'modifyDeviceCallback'))) {
|
|
self::$_device = $this->_driver->modifyDeviceCallback(self::$_device);
|
|
}
|
|
}
|
|
|
|
// Save the device now that we know it is at least allowed to connect,
|
|
// or it has connected successfully at least once in the past.
|
|
self::$_device->save();
|
|
if (is_callable(array($this->_driver, 'deviceCallback'))) {
|
|
$callback_ret = $this->_driver->deviceCallback(self::$_device);
|
|
if ($callback_ret !== true) {
|
|
$msg = sprintf(
|
|
'The device %s was disallowed for user %s per policy settings.',
|
|
self::$_device->id,
|
|
self::$_device->user);
|
|
self::$_logger->err($msg);
|
|
if ($version > self::VERSION_TWELVEONE) {
|
|
// Use a status code here, since the device has already
|
|
// connected.
|
|
$this->_globalError = $callback_ret;
|
|
return false;
|
|
} else {
|
|
throw new Horde_Exception_AuthenticationFailure($msg, $callback_ret);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Lastly, check if the device has been set to blocked.
|
|
if (self::$_device->blocked) {
|
|
$msg = sprintf(
|
|
'The device %s was blocked.',
|
|
self::$_device->id);
|
|
self::$_logger->err($msg);
|
|
if ($version > self::VERSION_TWELVEONE) {
|
|
$this->_globalError = Horde_ActiveSync_Status::DEVICE_BLOCKED_FOR_USER;
|
|
return false;
|
|
} else {
|
|
throw new Horde_ActiveSync_Exception($msg);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Send the MS_Server-ActiveSync header.
|
|
*
|
|
* @return array Returns an array of the headers that were sent.
|
|
* @since 2.39.0
|
|
*/
|
|
public function activeSyncHeader()
|
|
{
|
|
$headers = array(
|
|
'Allow: OPTIONS,POST',
|
|
sprintf('Server: Horde_ActiveSync Library v%s', self::LIBRARY_VERSION),
|
|
'Public: OPTIONS,POST'
|
|
);
|
|
|
|
switch ($this->_maxVersion) {
|
|
case self::VERSION_TWOFIVE:
|
|
$headers[] = 'MS-Server-ActiveSync: 6.5.7638.1';
|
|
break;
|
|
case self::VERSION_TWELVE:
|
|
$headers[] = 'MS-Server-ActiveSync: 12.0';
|
|
break;
|
|
case self::VERSION_TWELVEONE:
|
|
$headers[] = 'MS-Server-ActiveSync: 12.1';
|
|
break;
|
|
case self::VERSION_FOURTEEN:
|
|
$headers[] = 'MS-Server-ActiveSync: 14.0';
|
|
break;
|
|
case self::VERSION_FOURTEENONE:
|
|
$headers[] = 'MS-Server-ActiveSync: 14.1';
|
|
break;
|
|
case self::VERSION_SIXTEEN:
|
|
$headers[] = 'MS-Server-ActiveSync: 16.0';
|
|
}
|
|
|
|
foreach ($headers as $hdr) {
|
|
header($hdr);
|
|
}
|
|
|
|
return $headers;
|
|
}
|
|
|
|
/**
|
|
* Send the protocol versions header.
|
|
*
|
|
* @return string The header that was sent. @since 2.39.0
|
|
*/
|
|
public function versionHeader()
|
|
{
|
|
$hdr = sprintf('MS-ASProtocolVersions: %s', $this->getSupportedVersions());
|
|
header($hdr);
|
|
|
|
return $hdr;
|
|
}
|
|
|
|
/**
|
|
* Return supported versions in a comma delimited string suitable for
|
|
* sending as the MS-ASProtocolVersions header.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getSupportedVersions()
|
|
{
|
|
return implode(',', array_slice(self::$_supportedVersions, 0, (array_search($this->_maxVersion, self::$_supportedVersions) + 1)));
|
|
}
|
|
|
|
/**
|
|
* Send protocol commands header.
|
|
*
|
|
* @return string The header that was sent. @since 2.39.0
|
|
*/
|
|
public function commandsHeader()
|
|
{
|
|
$hdr = sprintf('MS-ASProtocolCommands: %s', $this->getSupportedCommands());
|
|
header($hdr);
|
|
|
|
return $hdr;
|
|
}
|
|
|
|
/**
|
|
* Return the supported commands in a comma delimited string suitable for
|
|
* sending as the MS-ASProtocolCommands header.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getSupportedCommands()
|
|
{
|
|
switch ($this->_maxVersion) {
|
|
case self::VERSION_TWOFIVE:
|
|
return 'Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,ResolveRecipients,ValidateCert,Provision,Search,Ping';
|
|
|
|
case self::VERSION_TWELVE:
|
|
case self::VERSION_TWELVEONE:
|
|
case self::VERSION_FOURTEEN:
|
|
case self::VERSION_FOURTEENONE:
|
|
case self::VERSION_SIXTEEN:
|
|
return 'Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,Search,Settings,Ping,ItemOperations,Provision,ResolveRecipients,ValidateCert';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send provision header
|
|
*/
|
|
public function provisionHeader()
|
|
{
|
|
header('HTTP/1.1 449 Retry after sending a PROVISION command');
|
|
}
|
|
|
|
/**
|
|
* Obtain the policy key header from the request.
|
|
*
|
|
* @return integer The policy key or '0' if not set.
|
|
*/
|
|
public function getPolicyKey()
|
|
{
|
|
// Policy key can come from header or encoded request parameters.
|
|
$this->_policykey = $this->_request->getHeader('X-MS-PolicyKey');
|
|
if (empty($this->_policykey)) {
|
|
$get = $this->getGetVars();
|
|
if (!empty($get['PolicyKey'])) {
|
|
$this->_policykey = $get['PolicyKey'];
|
|
} else {
|
|
$this->_policykey = 0;
|
|
}
|
|
}
|
|
|
|
return $this->_policykey;
|
|
}
|
|
|
|
/**
|
|
* Obtain the ActiveSync protocol version requested by the client headers.
|
|
*
|
|
* @return string The EAS version requested by the client.
|
|
*/
|
|
public function getProtocolVersion()
|
|
{
|
|
if (!isset(self::$_version)) {
|
|
self::$_version = $this->_request->getHeader('MS-ASProtocolVersion');
|
|
if (empty(self::$_version)) {
|
|
$get = $this->getGetVars();
|
|
self::$_version = empty($get['ProtVer']) ? '1.0' : $get['ProtVer'];
|
|
}
|
|
}
|
|
return self::$_version;
|
|
}
|
|
|
|
/**
|
|
* Return the GET variables passed from the device, decoding from
|
|
* base64 if needed.
|
|
*
|
|
* @return array A hash of get variables => values.
|
|
*/
|
|
public function getGetVars()
|
|
{
|
|
if (!empty($this->_get)) {
|
|
return $this->_get;
|
|
}
|
|
|
|
$results = array();
|
|
$get = $this->_request->getGetVars();
|
|
|
|
// Do we need to decode the request parameters?
|
|
if (!isset($get['Cmd']) && !isset($get['DeviceId']) && !isset($get['DeviceType'])) {
|
|
$serverVars = $this->_request->getServerVars();
|
|
if (isset($serverVars['QUERY_STRING']) && strlen($serverVars['QUERY_STRING']) >= 10) {
|
|
$results = Horde_ActiveSync_Utils::decodeBase64($serverVars['QUERY_STRING']);
|
|
// Normalize values.
|
|
switch ($results['DeviceType']) {
|
|
case 'PPC':
|
|
$results['DeviceType'] = 'PocketPC';
|
|
break;
|
|
case 'SP':
|
|
$results['DeviceType'] = 'SmartPhone';
|
|
break;
|
|
case 'WP':
|
|
case 'WP8':
|
|
$results['DeviceType'] = 'WindowsPhone';
|
|
break;
|
|
case 'android':
|
|
case 'android40':
|
|
$results['DeviceType'] = 'android';
|
|
}
|
|
$this->_get = $results;
|
|
}
|
|
} else {
|
|
$this->_get = $get;
|
|
}
|
|
|
|
return $this->_get;
|
|
}
|
|
|
|
/**
|
|
* Return any global errors that occured during initial connection.
|
|
*
|
|
* @since 2.4.0
|
|
* @return mixed A Horde_ActiveSync_Status:: constant of boolean false if
|
|
* no errors.
|
|
*/
|
|
public function checkGlobalError()
|
|
{
|
|
return $this->_globalError;
|
|
}
|
|
|
|
/**
|
|
* Send the content type header.
|
|
*
|
|
*/
|
|
public function contentTypeHeader($content_type = null)
|
|
{
|
|
if (!empty($content_type)) {
|
|
header('Content-Type: ' . $content_type);
|
|
return;
|
|
}
|
|
if ($this->_multipart) {
|
|
header('Content-Type: application/vnd.ms-sync.multipart');
|
|
} else {
|
|
header('Content-Type: application/vnd.ms-sync.wbxml');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send the OPTIONS request response headers.
|
|
*/
|
|
protected function _handleOptionsRequest()
|
|
{
|
|
$as_headers = implode("\r\n", $this->activeSyncHeader());
|
|
$version_header = $this->versionHeader();
|
|
$cmd_header = $this->commandsHeader();
|
|
self::$_logger->meta(sprintf(
|
|
"Returning OPTIONS response:\r\n%s\r\n%s\r\n%s",
|
|
$as_headers, $version_header, $cmd_header)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Return the number of bytes corresponding to the requested trunction
|
|
* constant. This applies to MIMETRUNCATION only.
|
|
*
|
|
* @param integer $truncation The constant.
|
|
*
|
|
* @return integer|boolean Either the size, in bytes, to truncate or
|
|
* falso if no truncation.
|
|
* @since 2.20.0
|
|
*/
|
|
public static function getMIMETruncSize($truncation)
|
|
{
|
|
switch($truncation) {
|
|
case Horde_ActiveSync::TRUNCATION_ALL:
|
|
return 0;
|
|
case Horde_ActiveSync::TRUNCATION_1:
|
|
return 4096;
|
|
case Horde_ActiveSync::TRUNCATION_2:
|
|
return 5120;
|
|
case Horde_ActiveSync::TRUNCATION_3:
|
|
return 7168;
|
|
case Horde_ActiveSync::TRUNCATION_4:
|
|
return 10240;
|
|
case Horde_ActiveSync::TRUNCATION_5:
|
|
return 20480;
|
|
case Horde_ActiveSync::TRUNCATION_6:
|
|
return 51200;
|
|
case Horde_ActiveSync::TRUNCATION_7:
|
|
return 102400;
|
|
case Horde_ActiveSync::TRUNCATION_8:
|
|
return false;
|
|
default:
|
|
return 1024; // Default to 1Kb
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the number of bytes corresponding to the requested trunction
|
|
* constant.
|
|
*
|
|
* @param integer $truncation The constant.
|
|
*
|
|
* @return integer|boolean Either the size, in bytes, to truncate or
|
|
* falso if no truncation.
|
|
*
|
|
*/
|
|
public static function getTruncSize($truncation)
|
|
{
|
|
switch($truncation) {
|
|
case Horde_ActiveSync::TRUNCATION_ALL:
|
|
return 0;
|
|
case Horde_ActiveSync::TRUNCATION_1:
|
|
return 512;
|
|
case Horde_ActiveSync::TRUNCATION_2:
|
|
return 1024;
|
|
case Horde_ActiveSync::TRUNCATION_3:
|
|
return 2048;
|
|
case Horde_ActiveSync::TRUNCATION_4:
|
|
return 5120;
|
|
case Horde_ActiveSync::TRUNCATION_5:
|
|
return 10240;
|
|
case Horde_ActiveSync::TRUNCATION_6:
|
|
return 20480;
|
|
case Horde_ActiveSync::TRUNCATION_7:
|
|
return 51200;
|
|
case Horde_ActiveSync::TRUNCATION_8:
|
|
return 102400;
|
|
case Horde_ActiveSync::TRUNCATION_9:
|
|
case Horde_ActiveSync::TRUNCATION_NONE: // @deprecated
|
|
return false;
|
|
default:
|
|
return 1024; // Default to 1Kb
|
|
}
|
|
}
|
|
|
|
}
|