1: <?php
2: /**
3: * Base class for handling ActiveSync requests
4: *
5: * Copyright 2009-2012 Horde LLC (http://www.horde.org/)
6: *
7: * @author Michael J. Rubinsky <mrubinsk@horde.org>
8: * @package ActiveSync
9: */
10: /**
11: * Zarafa Deutschland GmbH, www.zarafaserver.de
12: * This file is distributed under GPL-2.0.
13: * Consult COPYING file for details
14: */
15: abstract class Horde_ActiveSync_Request_Base
16: {
17: /**
18: * Driver for communicating with the backend datastore.
19: *
20: * @var Horde_ActiveSync_Driver_Base
21: */
22: protected $_driver;
23:
24: /**
25: * Encoder
26: *
27: * @var Horde_ActiveSync_Wbxml_Encoder
28: */
29: protected $_encoder;
30:
31: /**
32: * Decoder
33: *
34: * @var Horde_ActiveSync_Wbxml_Decoder
35: */
36: protected $_decoder;
37:
38: /**
39: * Request object
40: *
41: * @var Horde_Controller_Request_Http
42: */
43: protected $_request;
44:
45: /**
46: * Whether we require provisioned devices.
47: * Valid values are true, false, or Horde_ActiveSync::PROVISIONING_LOOSE.
48: * Loose allows devices that don't know about provisioning to continue to
49: * function, but requires devices that are capable to be provisioned.
50: *
51: * @var mixed
52: */
53: protected $_provisioning;
54:
55: /**
56: * The ActiveSync Version
57: *
58: * @var string
59: */
60: protected $_version;
61:
62: /**
63: * Used to track what error code to send back to PIM on failure
64: *
65: * @var integer
66: */
67: protected $_statusCode = 0;
68:
69: /**
70: * State object
71: *
72: * @var Horde_ActiveSync_State_Base
73: */
74: protected $_state;
75:
76: /**
77: * ActiveSync server
78: *
79: * @var Horde_ActiveSync
80: */
81: protected $_activeSync;
82:
83: /**
84: * Logger
85: *
86: * @var Horde_Log_Logger
87: */
88: protected $_logger;
89:
90: /**
91: * The device info
92: *
93: * @var stdClass
94: */
95: protected $_device;
96:
97: /**
98: * Const'r
99: *
100: * @param Horde_ActiveSync_Driver $driver The backend driver
101: * @param Horde_ActiveSync_Wbxml_Decoder $decoder The Wbxml decoder
102: * @param Horde_ActiveSync_Wbxml_Endcodder $encdoer The Wbxml encoder
103: * @param Horde_Controller_Request_Http $request The request object
104: * @param string $provisioning Is provisioning required?
105: *
106: * @return Horde_ActiveSync
107: */
108: public function __construct(Horde_ActiveSync_Driver_Base $driver,
109: Horde_ActiveSync_Wbxml_Decoder $decoder,
110: Horde_ActiveSync_Wbxml_Encoder $encoder,
111: Horde_Controller_Request_Http $request,
112: Horde_ActiveSync $as,
113: $device,
114: $provisioning)
115: {
116: /* Backend driver */
117: $this->_driver = $driver;
118:
119: /* server */
120: $this->_activeSync = $as;
121:
122: /* Wbxml handlers */
123: $this->_encoder = $encoder;
124: $this->_decoder = $decoder;
125:
126: /* The http request */
127: $this->_request = $request;
128:
129: /* Provisioning support */
130: $this->_provisioning = $provisioning;
131:
132: /* Get the state object */
133: $this->_state = &$driver->getStateObject();
134:
135: /* Device info */
136: $this->_device = $device;
137: }
138:
139: /**
140: * Ensure the PIM's policy key is current.
141: *
142: * @param integer $sentKey The policykey sent to us by the PIM
143: *
144: * @return boolean
145: */
146: public function checkPolicyKey($sentKey)
147: {
148: $this->_logger->debug('[' . $this->_device->id . '] Checking policykey for device '
149: . ' Key: ' . $sentKey
150: . ' User: ' . $this->_driver->getUser());
151:
152: $this->_device = $this->_state->loadDeviceInfo($this->_device->id, $this->_driver->getUser());
153:
154: // Use looseprovisioning?
155: if (empty($sentKey) && $this->_hasBrokenProvisioning() &&
156: $this->_provisioning == Horde_ActiveSync::PROVISIONING_LOOSE) {
157: $sentKey = null;
158: }
159:
160: // Don't attempt if we don't care
161: if ($this->_provisioning !== false) {
162: $state = $this->_driver->getStateObject();
163: $storedKey = $state->getPolicyKey($this->_device->id);
164: $this->_logger->debug('[' . $this->_device->id . '] Stored key: ' . $storedKey);
165:
166: /* Loose provsioning should allow a blank key */
167: if ((empty($storedKey) || $storedKey != $sentKey) &&
168: ($this->_provisioning !== Horde_ActiveSync::PROVISIONING_LOOSE ||
169: ($this->_provisioning === Horde_ActiveSync::PROVISIONING_LOOSE && !is_null($sentKey)))) {
170:
171: Horde_ActiveSync::provisioningRequired();
172: return false;
173: }
174: }
175:
176: $this->_logger->debug('Policykey: ' . $sentKey . ' verified.');
177:
178: return true;
179: }
180:
181: public function setLogger(Horde_Log_Logger $logger)
182: {
183: $this->_logger = $logger;
184: }
185:
186: /**
187: *
188: * @param string $version
189: * @param string $devId
190: */
191: public function handle()
192: {
193: $this->_version = $this->_activeSync->getProtocolVersion();
194: $this->_logger->info('Request being handled for device: ' . $this->_device->id . ' Supporting protocol version: ' . $this->_version);
195: }
196:
197: /**
198: * Utility function to help determine if a device has broken provisioning.
199: * This is impossible to get 100% right since versions of Android that
200: * are broken and versions that are not both use the same User-Agent string
201: * (Android/0.3 for both 2.1, 2.2 and even 2.3). We err on the side
202: * of device compatibility at the expense of not being able to provision
203: * some non-broken android devices when provisioning is set to
204: * Horde_ActiveSync::PROVISIONING_LOOSE.
205: *
206: * @TODO This should be added to a device object, once we implement
207: * Horde_ActiveSync_Device API.
208: *
209: * @return boolean
210: */
211: protected function _hasBrokenProvisioning()
212: {
213: if (strpos($this->_device->userAgent, 'Android') !== false) {
214: if (preg_match('@EAS[/-]{0,1}([.0-9]{2,})@', $this->_device->userAgent, $matches)) {
215: return ($matches[1] < 1.2);
216: }
217: return true;
218: }
219:
220: // WP7 not only doesn't support all EAS 2.5 security poliices, it flat
221: // out refuses to notify the server of a partial acceptance and just
222: // completely fails.
223: if (strpos($this->_device->userAgent, 'MSFT-WP/7') !== false) {
224: return true;
225: }
226:
227: // Not an android device - enforce provisioning if needed.
228: return false;
229: }
230:
231: /**
232: * Clean up after initial pairing. Initial pairing can happen either as a
233: * result of either a FOLDERSYNC or PROVISION command, depending on the
234: * device capabilities.
235: *
236: * @TODO Move this to a device object??
237: */
238: protected function _cleanUpAfterPairing()
239: {
240: // Android sends a bogus device id of 'validate' during initial
241: // handshake. This data is never used again, and the resulting
242: // FOLDERSYNC response is ignored by the client. Remove the entry,
243: // to avoid having 2 device entries for every android client.
244: if ($this->_device->id == 'validate') {
245: $this->_state->removeState(null, 'validate');
246: }
247: }
248:
249: }
250: