1: <?php
2: /**
3: * The Horde_SyncMl_State class provides a SyncML state object.
4: *
5: * Copyright 2003-2012 Horde LLC (http://www.horde.org/)
6: *
7: * See the enclosed file COPYING for license information (LGPL). If you
8: * did not receive this file, see http://www.horde.org/licenses/lgpl21.
9: *
10: * @author Anthony Mills <amills@pyramid6.com>
11: * @author Jan Schneider <jan@horde.org>
12: * @package SyncMl
13: */
14: class Horde_SyncMl_State
15: {
16: /**
17: * Id of this SyncML session.
18: *
19: * This is not to confuse with the PHP session id, though it is part of
20: * the generated PHP session id.
21: *
22: * @var string
23: */
24: public $sessionID;
25:
26: /**
27: * Id of the current message.
28: *
29: * @var integer
30: */
31: public $messageID;
32:
33: /**
34: * The target URI as sent by the client.
35: *
36: * This is normally the URL of the RPC server. However the client is
37: * free to send anything.
38: *
39: * @var string
40: */
41: public $targetURI;
42:
43: /**
44: * The source URI as sent by the client.
45: *
46: * Can be used to identify the client and is part of the PHP session id.
47: *
48: * @var string
49: */
50: public $sourceURI;
51:
52: /**
53: * SyncML protocol version.
54: *
55: * 0 for SyncML 1.0, 1 for SyncML 1.1, etc.
56: *
57: * @var integer
58: */
59: public $version;
60:
61: /**
62: * Username used to authenticate with the backend.
63: *
64: * @var string
65: */
66: public $user;
67:
68: /**
69: * Whether this session has authenticated successfully.
70: *
71: * @var boolean
72: */
73: public $authenticated = false;
74:
75: /**
76: * <SyncML> namespace uri.
77: *
78: * @var string
79: */
80: protected $_uri;
81:
82: /**
83: * <Meta> namespace uri.
84: *
85: * @var string
86: */
87: public $uriMeta;
88:
89: /**
90: * <DevInf> namespace uri.
91: *
92: * @var string
93: */
94: public $uriDevInf;
95:
96: /**
97: * Whether WBXML encoding is used.
98: *
99: * @var boolean
100: */
101: public $wbxml = false;
102:
103: /**
104: * The maximum allowed message size in bytes.
105: *
106: * @todo Change to PHP_INT_MAX.
107: *
108: * @var integer
109: */
110: public $maxMsgSize = 1000000000;
111:
112: /**
113: * Array of Horde_SyncMl_Sync objects.
114: *
115: * @var array
116: */
117: protected $_syncs = array();
118:
119: /**
120: * The list of all server changes being sent to the client as a reference
121: * for Status responses from the client.
122: *
123: * @var array
124: */
125: public $serverChanges = array();
126:
127: /**
128: * Name of the appropriate device driver.
129: *
130: * @var string
131: */
132: protected $_deviceDriver;
133:
134: /**
135: * Device info provided by the SyncML DevInf data.
136: *
137: * @var Horde_SyncMl_DeviceInfo
138: */
139: public $deviceInfo;
140:
141: /**
142: * Current sync element sent from client.
143: *
144: * Stored in state if one element is split into multiple message packets.
145: *
146: * @var Horde_SyncMl_SyncElement
147: */
148: public $curSyncItem;
149:
150: /**
151: * Flag that is set if the client sends a Final but we are not finished
152: * with the current package and thus can't final this package yet.
153: *
154: * @var boolean
155: */
156: public $delayedFinal = false;
157:
158: /**
159: * Constructor.
160: */
161: public function __construct($sourceURI, $user, $sessionID)
162: {
163: $this->sourceURI = $sourceURI;
164: $this->user = $user;
165: $this->sessionID = $sessionID;
166:
167: /* Create empty dummy device info. Will be replaced with real DevInf
168: * information if provided by the client. */
169: $this->deviceInfo = new Horde_SyncMl_DeviceInfo();
170: }
171:
172: /**
173: * Returns the <DevInf><VerDTD> content based on the protocol version.
174: */
175: public function getVerDTD()
176: {
177: switch ($this->version) {
178: case 1:
179: return '1.1';
180: case 2:
181: return '1.2';
182: default:
183: return '1.0';
184: }
185: }
186:
187: /**
188: * Returns the DevInf URI based on the protocol version.
189: */
190: public function getDevInfURI()
191: {
192: switch ($this->version) {
193: case 1:
194: return './devinf11';
195: case 2:
196: return './devinf12';
197: default:
198: return './devinf10';
199: }
200: }
201:
202: /**
203: * Returns the protocol name based on the protocol version.
204: */
205: public function getProtocolName()
206: {
207: switch ($this->version) {
208: case 1:
209: return 'SyncML/1.1';
210: case 2:
211: return 'SyncML/1.2';
212: default:
213: return 'SyncML/1.0';
214: }
215: }
216:
217: /**
218: * Sets the protocol version
219: *
220: * @param integer $version The protocol version: 0 for SyncML 1.0, 1 for
221: * SyncML 1.1 etc.
222: */
223: public function setVersion($version)
224: {
225: switch ($version) {
226: case 1:
227: $this->_uri = Horde_SyncMl::NAME_SPACE_URI_SYNCML_1_1;
228: $this->uriMeta = Horde_SyncMl::NAME_SPACE_URI_METINF_1_1;
229: $this->uriDevInf = Horde_SyncMl::NAME_SPACE_URI_DEVINF_1_1;
230: break;
231: case 2:
232: $this->_uri = Horde_SyncMl::NAME_SPACE_URI_SYNCML_1_2;
233: $this->uriMeta = Horde_SyncMl::NAME_SPACE_URI_METINF_1_2;
234: $this->uriDevInf = Horde_SyncMl::NAME_SPACE_URI_DEVINF_1_2;
235: break;
236: default:
237: $this->_uri = Horde_SyncMl::NAME_SPACE_URI_SYNCML;
238: $this->uriMeta = Horde_SyncMl::NAME_SPACE_URI_METINF;
239: $this->uriDevInf = Horde_SyncMl::NAME_SPACE_URI_DEVINF;
240: break;
241: }
242:
243: $this->version = $version;
244: }
245:
246: /**
247: * Returns the namespace URI for the <SyncML> element.
248: *
249: * @return string The namespace URI to use, if any.
250: */
251: public function getURI()
252: {
253: /* The non WBXML devices (notably SonyEricsson and Funambol) seem to
254: * get confused by a <SyncML xmlns="syncml:SYNCML1.1"> element. They
255: * require just <SyncML>. So don't use a namespace for non-wbxml
256: * devices. */
257: if ($this->wbxml || $this->version > 0) {
258: return $this->_uri;
259: } else {
260: return '';
261: }
262: }
263:
264: /**
265: * Returns a Horde_SyncMl_Device instance for the device used in this session.
266: *
267: * @return Horde_SyncMl_Device A Horde_SyncMl_Device instance.
268: */
269: public function getDevice()
270: {
271: if (empty($this->_deviceDriver)) {
272: $si = $this->sourceURI;
273: $di = $this->deviceInfo;
274:
275: if (stristr($si, 'sync4j') !== false ||
276: stristr($si, 'sc-pim') !== false ||
277: stristr($si, 'fol-') !== false ||
278: stristr($si, 'fwm-') !== false ||
279: stristr($si, 'fbb-') !== false) {
280: $this->_deviceDriver = 'Sync4j';
281: } elseif (!empty($di->Man) &&
282: (stristr($di->Man, 'Sony Ericsson') !== false ||
283: stristr($di->Mod, 'A1000') !== false)) {
284: /* The Morola A1000 has a similar (UIQ) firmware as the
285: * P800: */
286: $this->_deviceDriver = 'P800';
287: } elseif (!empty($di->Man) &&
288: stristr($di->Man, 'synthesis') !== false) {
289: $this->_deviceDriver = 'Synthesis';
290: } elseif (!empty($di->Man) &&
291: stristr($di->Man, 'nokia') !== false) {
292: $this->_deviceDriver = 'Nokia';
293: } elseif (stristr($si, 'fmz-') !== false) {
294: $this->_deviceDriver = 'Sync4JMozilla';
295: } else {
296: $this->_deviceDriver = 'default';
297: }
298: }
299:
300: return Horde_SyncMl_Device::factory($this->_deviceDriver);
301: }
302:
303: /**
304: * @param string $target
305: * @param Horde_SyncMl_Sync $sync
306: */
307: public function setSync($target, $sync)
308: {
309: $this->_syncs[$target] = $sync;
310: }
311:
312: /**
313: * @param string $target
314: * @return Horde_SyncMl_Sync
315: */
316: public function getSync($target)
317: {
318: if (isset($this->_syncs[$target])) {
319: return $this->_syncs[$target];
320: } else {
321: return false;
322: }
323: }
324:
325: /**
326: * @return array
327: */
328: public function getSyncs()
329: {
330: return $this->_syncs;
331: }
332:
333: /**
334: * Returns whether there are any pending elements that have not been sent
335: * to due to message size restrictions. These will be sent int the next
336: * message.
337: *
338: * @return boolean True if there are pending elements that have yet to be
339: * sent.
340: */
341: public function hasPendingSyncs()
342: {
343: if (is_array($this->_syncs)) {
344: foreach ($this->_syncs as $sync) {
345: if ($sync->hasPendingElements()) {
346: return true;
347: }
348: }
349: }
350: return false;
351: }
352:
353: /**
354: * Returns all syncs which have pending elements left.
355: *
356: * @return array Array of TargetLocURIs which can be used as a key in
357: * getSync() calls.
358: */
359: public function getPendingSyncs()
360: {
361: $pending = array();
362: if (is_array($this->_syncs)) {
363: foreach ($this->_syncs as $target => $sync) {
364: if ($sync->hasPendingElements()) {
365: $pending[] = $target;
366: }
367: }
368: }
369: return $pending;
370: }
371:
372: /**
373: * Returns whether all syncs are in completed state or no syncs are
374: * present.
375: *
376: * @return boolean True if all syncs are in completed state.
377: */
378: public function isAllSyncsComplete()
379: {
380: if (is_array($this->_syncs)) {
381: foreach ($this->_syncs as $target => $sync) {
382: if (!$sync->isComplete()) {
383: return false;
384: }
385: }
386: }
387: return true;
388: }
389:
390: /**
391: * Propagates final tags here and then further to every sync.
392: *
393: * This allows the sync objects to determine if they are complete.
394: */
395: public function handleFinal(&$output, $debug = false)
396: {
397: if (is_array($this->_syncs)) {
398: foreach (array_keys($this->_syncs) as $t) {
399: $this->_syncs[$t]->handleFinal($output, $debug);
400: }
401: }
402: }
403: }
404: