1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
27: class Horde_SyncMl_ContentHandler
28: {
29: 30: 31: 32: 33: 34:
35: protected $_Stack = array();
36:
37: 38: 39:
40: protected $_chars;
41:
42: 43: 44: 45: 46: 47:
48: protected $_currentCommand;
49:
50: 51: 52:
53: protected $_gotFinal = false;
54:
55: protected $_xmlWriter;
56:
57: protected $_wbxmlparser = null;
58:
59: 60: 61: 62: 63: 64: 65:
66: protected $_respURI;
67:
68: public $debug = false;
69:
70: public function __construct()
71: {
72: 73: 74:
75: $GLOBALS['message_expectresponse'] = false;
76: }
77:
78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89:
90: public function process($request, $contentType, $respURI = null)
91: {
92: $isWBXML = $contentType =='application/vnd.syncml+wbxml';
93: $this->_respURI = $respURI;
94:
95: 96: 97:
98: ob_start();
99:
100: $GLOBALS['backend']->logFile(Horde_SyncMl_Backend::LOGFILE_CLIENTMESSAGE, $request, $isWBXML);
101:
102: if (!$isWBXML) {
103:
104:
105:
106: if (preg_match('/^\s*<\?xml[^>]*encoding\s*=\s*"([^"]*)"/i',
107: $request, $m)) {
108: $charset = $m[1];
109: } else {
110: $charset = 'UTF-8';
111: }
112:
113: $GLOBALS['backend']->setCharset($charset);
114:
115:
116: $this->_xmlWriter = &Horde_SyncMl_XmlOutput::singleton();
117: 118:
119: $this->_xmlWriter->init(new Horde_Xml_Wbxml_ContentHandler());
120:
121:
122: $parser = xml_parser_create_ns($charset);
123: xml_set_object($parser, $this);
124: xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
125: xml_set_element_handler($parser, '_startElement', '_endElement');
126: xml_set_character_data_handler($parser, '_characters');
127: xml_set_processing_instruction_handler($parser, '');
128: xml_set_external_entity_ref_handler($parser, '');
129:
130:
131: if (!xml_parse($parser, $request)) {
132: $s = sprintf('XML error: %s at line %d',
133: xml_error_string(xml_get_error_code($parser)),
134: xml_get_current_line_number($parser));
135: $GLOBALS['backend']->logMessage($s, 'ERR');
136: xml_parser_free($parser);
137: return new PEAR_Error($s);
138: }
139:
140: xml_parser_free($parser);
141:
142: } else {
143: 144:
145: $this->_wbxmlparser = new Horde_Xml_Wbxml_Decoder();
146: $this->_wbxmlparser->setContentHandler($this);
147:
148:
149: $this->_xmlWriter = &Horde_SyncMl_XmlOutput::singleton();
150: $this->_xmlWriter->init(new Horde_Xml_Wbxml_Encoder());
151:
152:
153:
154: $this->_wbxmlparser->decode($request);
155: }
156:
157: $id = @session_id();
158: $sessionclose = empty($id);
159:
160: $output = $this->getOutput();
161: if (!$isWBXML) {
162: $output = '<?xml version="1.0" encoding="' . $charset . '"?>' . $output;
163: }
164: $GLOBALS['backend']->logFile(Horde_SyncMl_Backend::LOGFILE_SERVERMESSAGE, $output, $isWBXML, $sessionclose);
165:
166: 167:
168: $errorLogging = ob_get_clean();
169:
170: if (!empty($errorLogging)) {
171: $GLOBALS['backend']->logMessage('Caught output: ' . $errorLogging, 'WARN');
172: }
173:
174: return $output;
175: }
176:
177: 178: 179: 180: 181: 182:
183:
184: 185: 186: 187: 188:
189: public function getOutput()
190: {
191: return $this->_xmlWriter->getOutput();
192: }
193:
194: 195: 196:
197: protected function _startElement($parser, $tag, $attributes)
198: {
199: list($uri, $name) = $this->_splitURI($tag);
200: $this->startElement($uri, $name, $attributes);
201: }
202:
203: 204: 205:
206: protected function _characters($parser, $chars)
207: {
208: $this->characters($chars);
209: }
210:
211: 212: 213:
214: protected function _endElement($parser, $tag)
215: {
216: list($uri, $name) = $this->_splitURI($tag);
217: $this->endElement($uri, $name);
218: }
219:
220: 221: 222:
223: protected function _splitURI($tag)
224: {
225: $parts = explode(':', $tag);
226: $name = array_pop($parts);
227: $uri = implode(':', $parts);
228: return array($uri, $name);
229: }
230:
231: 232: 233:
234: public function startElement($uri, $element, $attrs)
235: {
236: $this->_Stack[] = $element;
237:
238:
239: if (count($this->_Stack) == 1) {
240: return;
241: }
242:
243:
244: if ($this->_Stack[1] == 'SyncHdr') {
245: if (count($this->_Stack) == 2) {
246: $this->_currentCommand = new Horde_SyncMl_Command_SyncHdr($this->_xmlWriter);
247: }
248: $this->_currentCommand->startElement($uri, $element, $attrs);
249: } else {
250: switch (count($this->_Stack)) {
251: case 2:
252:
253: break;
254: case 3:
255:
256:
257: $this->_currentCommand = &Horde_SyncMl_Command::factory($element,$this->_xmlWriter);
258: $this->_currentCommand->startElement($uri, $element, $attrs);
259: break;
260: default:
261:
262:
263: $this->_currentCommand->startElement($uri, $element, $attrs);
264: break;
265: }
266: }
267: }
268:
269: 270: 271:
272: public function endElement($uri, $element)
273: {
274:
275: if (count($this->_Stack) == 1) {
276: return;
277: }
278:
279: if ($this->_Stack[1] == 'SyncHdr') {
280: switch (count($this->_Stack)) {
281: case 2:
282:
283: $this->handleHeader($this->_currentCommand);
284: if ($this->debug) {
285: var_dump($this->_currentCommand);
286: }
287: unset($this->_currentCommand);
288: break;
289: default:
290:
291: $this->_currentCommand->endElement($uri, $element);
292: break;
293: }
294: } else {
295: switch (count($this->_Stack)) {
296: case 2:
297:
298: $this->handleEnd();
299: break;
300: case 3:
301:
302:
303: $this->_currentCommand->endElement($uri, $element);
304: $this->handleCommand($this->_currentCommand);
305: if ($this->debug) {
306: var_dump($this->_currentCommand);
307: }
308: unset($this->_currentCommand);
309: break;
310: default:
311:
312:
313: $this->_currentCommand->endElement($uri, $element);
314: break;
315: }
316: }
317:
318: if (isset($this->_chars)) {
319: unset($this->_chars);
320: }
321:
322: array_pop($this->_Stack);
323: }
324:
325: 326: 327:
328: public function characters($str)
329: {
330: if (isset($this->_currentCommand)) {
331: $this->_currentCommand->characters($str);
332: } else {
333: if (isset($this->_chars)) {
334: $this->_chars = $this->_chars . $str;
335: } else {
336: $this->_chars = $str;
337: }
338: }
339: }
340:
341: 342: 343: 344: 345: 346:
347:
348: 349: 350: 351: 352:
353: public function handleHeader(&$hdr)
354: {
355: if (is_object($this->_wbxmlparser)) {
356: 357:
358: $this->_xmlWriter->output->setVersion($this->_wbxmlparser->getVersion());
359: $this->_xmlWriter->output->setCharset($this->_wbxmlparser->getCharsetStr());
360: $GLOBALS['backend']->setCharset($this->_wbxmlparser->getCharsetStr());
361: }
362:
363:
364: $hdr->setupState();
365: $state = $GLOBALS['backend']->state;
366: $state->wbxml = $this->_xmlWriter->isWBXML();
367:
368:
369: if (!$state->authenticated) {
370: $auth = $GLOBALS['backend']->checkAuthentication(
371: $hdr->user, $hdr->credData, $hdr->credFormat, $hdr->credType);
372: if ($auth !== false) {
373: $state->authenticated = true;
374: $statuscode = Horde_SyncMl::RESPONSE_AUTHENTICATION_ACCEPTED;
375: $state->user = $auth;
376: $GLOBALS['backend']->setUser($auth);
377: } else {
378: if (!$hdr->credData) {
379: $statuscode = Horde_SyncMl::RESPONSE_CREDENTIALS_MISSING;
380: } else {
381: $statuscode = Horde_SyncMl::RESPONSE_INVALID_CREDENTIALS;
382: }
383: $GLOBALS['backend']->logMessage('Invalid authentication', 'DEBUG');
384: }
385: } else {
386: $statuscode = Horde_SyncMl::RESPONSE_OK;
387: $GLOBALS['backend']->setUser($state->user);
388: }
389:
390:
391: $this->_xmlWriter->outputInit();
392:
393:
394: $this->_xmlWriter->outputHeader($this->_respURI);
395:
396:
397: $this->_xmlWriter->outputBodyStart();
398:
399:
400: $this->_xmlWriter->outputStatus('0', 'SyncHdr', $statuscode,
401: $state->targetURI,
402: $state->sourceURI);
403:
404:
405: $str = 'Authenticated: ' . ($state->authenticated ? 'yes' : 'no')
406: . '; version: ' . $state->getVerDTD()
407: . '; message ID: ' . $state->messageID
408: . '; source URI: ' . $state->sourceURI
409: . '; target URI: ' . $state->targetURI
410: . '; user: ' . $state->user
411: . '; charset: ' . $GLOBALS['backend']->getCharset()
412: . '; wbxml: ' . ($state->wbxml ? 'yes' : 'no');
413:
414: $GLOBALS['backend']->logMessage($str, 'DEBUG');
415: }
416:
417: 418: 419: 420: 421:
422: public function handleCommand(&$cmd)
423: {
424: $name = $cmd->getCommandName();
425: if ($name != 'Status' && $name != 'Map' && $name != 'Final' &&
426: $name != 'Sync' && $name != 'Results') {
427:
428: $GLOBALS['message_expectresponse'] = true;
429: }
430: if ($name == 'Final') {
431: $this->_gotFinal = true;
432: }
433:
434: $cmd->handleCommand($this->debug);
435: }
436:
437: 438: 439: 440: 441:
442: public function handleEnd()
443: {
444: global $messageFull;
445:
446: $state = $GLOBALS['backend']->state;
447:
448: 449:
450: if ($messageFull || $state->hasPendingSyncs()) {
451:
452: $GLOBALS['message_expectresponse'] = true;
453: }
454:
455: if (!$messageFull &&
456: count($p = $state->getPendingSyncs()) > 0) {
457: foreach ($p as $pendingSync) {
458: if (!$messageFull) {
459: $GLOBALS['backend']->logMessage(
460: 'Continuing sync for syncType ' . $pendingSync, 'DEBUG');
461: $sync = &$state->getSync($pendingSync);
462: $sync->createSyncOutput($this->_xmlWriter);
463: }
464: }
465: }
466:
467: if (isset($state->curSyncItem)) {
468: $this->_xmlWriter->outputAlert(
469: Horde_SyncMl::ALERT_NO_END_OF_DATA,
470: $state->curSyncItem->sync->getClientLocURI(),
471: $state->curSyncItem->sync->getServerLocURI(),
472: $state->curSyncItem->sync->getServerAnchorLast(),
473: $state->curSyncItem->sync->getServerAnchorNext());
474: }
475:
476:
477: if ($this->_gotFinal) {
478: if (!$messageFull &&
479: !$state->hasPendingSyncs()) {
480:
481: $this->_xmlWriter->outputFinal();
482: $GLOBALS['backend']->logMessage('Sending <Final> to client', 'DEBUG');
483: $state->delayedFinal = false;
484: } else {
485: $GLOBALS['message_expectresponse'] = true;
486:
487: $state->delayedFinal = true;
488: }
489: } elseif ($state->delayedFinal) {
490: if (!$messageFull &&
491: !$state->hasPendingSyncs()) {
492:
493: $this->_xmlWriter->outputFinal();
494: $GLOBALS['backend']->logMessage(
495: 'Sending delayed <Final> to client', 'DEBUG');
496: $state->delayedFinal = false;
497: } else {
498: $GLOBALS['message_expectresponse'] = true;
499: }
500: }
501:
502:
503: $this->_xmlWriter->outputEnd();
504:
505: if ($this->_gotFinal &&
506: !$GLOBALS['message_expectresponse'] &&
507: $state->isAllSyncsComplete()) {
508: 509: 510:
511: foreach ($state->getSyncs() as $sync) {
512: $sync->closeSync();
513: }
514: $GLOBALS['backend']->logMessage('Session completed and closed', 'DEBUG');
515:
516:
517: $GLOBALS['backend']->sessionClose();
518: } else {
519: $GLOBALS['backend']->logMessage('Return message completed', 'DEBUG');
520: }
521: }
522: }
523: