1: <?php
2: 3: 4: 5: 6: 7:
8:
9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
20: class Horde_ActiveSync_Wbxml_Decoder extends Horde_ActiveSync_Wbxml
21: {
22:
23:
24: public $version;
25: public $publicid;
26: public $publicstringid;
27: public $charsetid;
28: public $stringtable;
29:
30: private $_attrcp = 0;
31: private $_ungetbuffer;
32:
33: 34: 35: 36: 37: 38:
39: public function ()
40: {
41: $this->version = $this->_getByte();
42: $this->publicid = $this->_getMBUInt();
43: if ($this->publicid == 0) {
44: $this->publicstringid = $this->_getMBUInt();
45: }
46: $this->charsetid = $this->_getMBUInt();
47: $this->stringtable = $this->_getStringTable();
48: }
49:
50: 51: 52: 53: 54:
55: public function setLogger(Horde_Log_Logger $logger)
56: {
57: $this->_logger = $logger;
58: }
59:
60: 61: 62: 63:
64: public function getElement()
65: {
66: $element = $this->getToken();
67:
68: switch ($element[Horde_ActiveSync_Wbxml::EN_TYPE]) {
69: case Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG:
70: return $element;
71: case Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG:
72: return $element;
73: case Horde_ActiveSync_Wbxml::EN_TYPE_CONTENT:
74: while (1) {
75: $next = $this->getToken();
76: if ($next == false) {
77: return false;
78: } elseif ($next[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_CONTENT) {
79: $element[Horde_ActiveSync_Wbxml::EN_CONTENT] .= $next[Horde_ActiveSync_Wbxml::EN_CONTENT];
80: } else {
81: $this->_ungetElement($next);
82: break;
83: }
84: }
85: return $element;
86: }
87:
88: return false;
89: }
90:
91: 92: 93: 94:
95: public function peek()
96: {
97: $element = $this->getElement();
98: $this->_ungetElement($element);
99:
100: return $element;
101: }
102:
103: 104: 105: 106: 107: 108: 109:
110: public function getElementStartTag($tag)
111: {
112: $element = $this->getToken();
113:
114: if ($element[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG &&
115: $element[Horde_ActiveSync_Wbxml::EN_TAG] == $tag) {
116:
117: return $element;
118: } else {
119: $this->_ungetElement($element);
120: }
121:
122: return false;
123: }
124:
125: 126: 127: 128: 129:
130: public function getElementEndTag()
131: {
132: $element = $this->getToken();
133: if ($element[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG) {
134: return $element;
135: } else {
136: $this->_logger->err('Unmatched end tag:');
137: $this->_logger->err(print_r($element, true));
138: $this->_ungetElement($element);
139: }
140:
141: return false;
142: }
143:
144: 145: 146: 147: 148:
149: public function getElementContent()
150: {
151: $element = $this->getToken();
152: if ($element[Horde_ActiveSync_Wbxml::EN_TYPE] == Horde_ActiveSync_Wbxml::EN_TYPE_CONTENT) {
153: return $element[Horde_ActiveSync_Wbxml::EN_CONTENT];
154: } else {
155: $this->_logger->err('Unmatched content:');
156: $this->_logger->err(print_r($element, true));
157: $this->_ungetElement($element);
158: }
159:
160: return false;
161: }
162:
163: 164: 165: 166: 167:
168: public function getToken()
169: {
170:
171: if ($this->_ungetbuffer) {
172: $element = $this->_ungetbuffer;
173: $this->_ungetbuffer = false;
174: return $element;
175: }
176:
177: $el = $this->_getToken();
178: $this->_logToken($el);
179:
180: return $el;
181: }
182:
183: 184: 185: 186: 187: 188: 189:
190: private function _logToken($el)
191: {
192: $spaces = str_repeat(' ', count($this->_logStack));
193: switch ($el[Horde_ActiveSync_Wbxml::EN_TYPE]) {
194: case Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG:
195: if ($el[Horde_ActiveSync_Wbxml::EN_FLAGS] & Horde_ActiveSync_Wbxml::EN_FLAGS_CONTENT) {
196: $this->_logger->debug('I ' . $spaces . ' <' . $el[Horde_ActiveSync_Wbxml::EN_TAG] . '>');
197: array_push($this->_logStack, $el[Horde_ActiveSync_Wbxml::EN_TAG]);
198: } else {
199: $this->_logger->debug('I ' . $spaces . ' <' . $el[Horde_ActiveSync_Wbxml::EN_TAG] . '/>');
200: }
201: break;
202: case Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG:
203: $tag = array_pop($this->_logStack);
204: $this->_logger->debug('I ' . $spaces . '</' . $tag . '>');
205: break;
206: case Horde_ActiveSync_Wbxml::EN_TYPE_CONTENT:
207: $this->_logger->debug('I ' . $spaces . ' ' . $el[Horde_ActiveSync_Wbxml::EN_CONTENT]);
208: break;
209: }
210: }
211:
212: 213: 214: 215: 216:
217: private function _getToken() {
218:
219:
220: $element = array();
221:
222: while (1) {
223: $byte = $this->_getByte();
224:
225: if (!isset($byte)) {
226: break;
227: }
228:
229: switch ($byte) {
230: case Horde_ActiveSync_Wbxml::SWITCH_PAGE:
231: $this->_tagcp = $this->_getByte();
232: continue;
233:
234: case Horde_ActiveSync_Wbxml::END:
235: $element[Horde_ActiveSync_Wbxml::EN_TYPE] = Horde_ActiveSync_Wbxml::EN_TYPE_ENDTAG;
236: return $element;
237:
238: case Horde_ActiveSync_Wbxml::ENTITY:
239: $entity = $this->_getMBUInt();
240: $element[Horde_ActiveSync_Wbxml::EN_TYPE] = Horde_ActiveSync_Wbxml::EN_TYPE_CONTENT;
241: $element[Horde_ActiveSync_Wbxml::EN_CONTENT] = $this->entityToCharset($entity);
242: return $element;
243:
244: case Horde_ActiveSync_Wbxml::STR_I:
245: $element[Horde_ActiveSync_Wbxml::EN_TYPE] = Horde_ActiveSync_Wbxml::EN_TYPE_CONTENT;
246: $element[Horde_ActiveSync_Wbxml::EN_CONTENT] = $this->_getTermStr();
247: return $element;
248:
249: case Horde_ActiveSync_Wbxml::LITERAL:
250: $element[Horde_ActiveSync_Wbxml::EN_TYPE] = Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG;
251: $element[Horde_ActiveSync_Wbxml::EN_TAG] = $this->_getStringTableEntry($this->_getMBUInt());
252: $element[Horde_ActiveSync_Wbxml::EN_FLAGS] = 0;
253: return $element;
254:
255: case Horde_ActiveSync_Wbxml::EXT_I_0:
256: case Horde_ActiveSync_Wbxml::EXT_I_1:
257: case Horde_ActiveSync_Wbxml::EXT_I_2:
258: $this->_getTermStr();
259:
260: continue;
261:
262: case Horde_ActiveSync_Wbxml::PI:
263:
264: $this->_getAttributes();
265: continue;
266:
267: case Horde_ActiveSync_Wbxml::LITERAL_C:
268: $element[Horde_ActiveSync_Wbxml::EN_TYPE] = Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG;
269: $element[Horde_ActiveSync_Wbxml::EN_TAG] = $this->_getStringTableEntry($this->_getMBUInt());
270: $element[Horde_ActiveSync_Wbxml::EN_FLAGS] = Horde_ActiveSync_Wbxml::EN_FLAGS_CONTENT;
271: return $element;
272:
273: case Horde_ActiveSync_Wbxml::EXT_T_0:
274: case Horde_ActiveSync_Wbxml::EXT_T_1:
275: case Horde_ActiveSync_Wbxml::EXT_T_2:
276: $this->_getMBUInt();
277:
278: continue;
279:
280: case Horde_ActiveSync_Wbxml::STR_T:
281: $element[Horde_ActiveSync_Wbxml::EN_TYPE] = Horde_ActiveSync_Wbxml::EN_TYPE_CONTENT;
282: $element[Horde_ActiveSync_Wbxml::EN_CONTENT] = $this->_getStringTableEntry($this->_getMBUInt());
283: return $element;
284:
285: case Horde_ActiveSync_Wbxml::LITERAL_A:
286: $element[Horde_ActiveSync_Wbxml::EN_TYPE] = Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG;
287: $element[Horde_ActiveSync_Wbxml::EN_TAG] = $this->_getStringTableEntry($this->_getMBUInt());
288: $element[Horde_ActiveSync_Wbxml::EN_ATTRIBUTES] = $this->_getAttributes();
289: $element[Horde_ActiveSync_Wbxml::EN_FLAGS] = Horde_ActiveSync_Wbxml::EN_FLAGS_ATTRIBUTES;
290: return $element;
291: case Horde_ActiveSync_Wbxml::EXT_0:
292: case Horde_ActiveSync_Wbxml::EXT_1:
293: case Horde_ActiveSync_Wbxml::EXT_2:
294: continue;
295:
296: case Horde_ActiveSync_Wbxml::OPAQUE:
297: $length = $this->_getMBUInt();
298: $element[Horde_ActiveSync_Wbxml::EN_TYPE] = Horde_ActiveSync_Wbxml::EN_TYPE_CONTENT;
299: $element[Horde_ActiveSync_Wbxml::EN_CONTENT] = $this->_getOpaque($length);
300: return $element;
301:
302: case Horde_ActiveSync_Wbxml::LITERAL_AC:
303: $element[Horde_ActiveSync_Wbxml::EN_TYPE] = Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG;
304: $element[Horde_ActiveSync_Wbxml::EN_TAG] = $this->_getStringTableEntry($this->_getMBUInt());
305: $element[Horde_ActiveSync_Wbxml::EN_ATTRIBUTES] = $this->_getAttributes();
306: $element[Horde_ActiveSync_Wbxml::EN_FLAGS] = Horde_ActiveSync_Wbxml::EN_FLAGS_ATTRIBUTES | Horde_ActiveSync_Wbxml::EN_FLAGS_CONTENT;
307: return $element;
308:
309: default:
310: $element[Horde_ActiveSync_Wbxml::EN_TYPE] = Horde_ActiveSync_Wbxml::EN_TYPE_STARTTAG;
311: $element[Horde_ActiveSync_Wbxml::EN_TAG] = $this->_getMapping($this->_tagcp, $byte & 0x3f);
312: $element[Horde_ActiveSync_Wbxml::EN_FLAGS] = ($byte & 0x80 ? Horde_ActiveSync_Wbxml::EN_FLAGS_ATTRIBUTES : 0) | ($byte & 0x40 ? Horde_ActiveSync_Wbxml::EN_FLAGS_CONTENT : 0);
313: if ($byte & 0x80) {
314: $element[Horde_ActiveSync_Wbxml::EN_ATTRIBUTES] = $this->_getAttributes();
315: }
316: return $element;
317: }
318: }
319: }
320:
321: 322: 323: 324: 325: 326: 327: 328:
329: public function _ungetElement($element)
330: {
331: if ($this->_ungetbuffer) {
332: $this->_logger->err('Double unget!');
333: }
334: $this->_ungetbuffer = $element;
335: }
336:
337: 338: 339: 340: 341:
342: private function _getAttributes()
343: {
344: $attributes = array();
345: $attr = '';
346:
347: while (1) {
348: $byte = $this->_getByte();
349: if (count($byte) == 0) {
350: break;
351: }
352:
353: switch($byte) {
354: case Horde_ActiveSync_Wbxml::SWITCH_PAGE:
355: $this->_attrcp = $this->_getByte();
356: break;
357:
358: case Horde_ActiveSync_Wbxml::END:
359: if ($attr != '') {
360: $attributes += $this->_splitAttribute($attr);
361: }
362: return $attributes;
363:
364: case Horde_ActiveSync_Wbxml::ENTITY:
365: $entity = $this->_getMBUInt();
366: $attr .= $this->entityToCharset($entity);
367: return $element;
368:
369: case Horde_ActiveSync_Wbxml::STR_I:
370: $attr .= $this->_getTermStr();
371: return $element;
372:
373: case Horde_ActiveSync_Wbxml::LITERAL:
374: if ($attr != '') {
375: $attributes += $this->_splitAttribute($attr);
376: }
377: $attr = $this->_getStringTableEntry($this->_getMBUInt());
378: return $element;
379:
380: case Horde_ActiveSync_Wbxml::EXT_I_0:
381: case Horde_ActiveSync_Wbxml::EXT_I_1:
382: case Horde_ActiveSync_Wbxml::EXT_I_2:
383: $this->_getTermStr();
384: continue;
385:
386: case Horde_ActiveSync_Wbxml::PI:
387: case Horde_ActiveSync_Wbxml::LITERAL_C:
388:
389: return false;
390:
391: case Horde_ActiveSync_Wbxml::EXT_T_0:
392: case Horde_ActiveSync_Wbxml::EXT_T_1:
393: case Horde_ActiveSync_Wbxml::EXT_T_2:
394: $this->_getMBUInt();
395: continue;
396:
397: case Horde_ActiveSync_Wbxml::STR_T:
398: $attr .= $this->_getStringTableEntry($this->_getMBUInt());
399: return $element;
400:
401: case Horde_ActiveSync_Wbxml::LITERAL_A:
402: return false;
403:
404: case Horde_ActiveSync_Wbxml::EXT_0:
405: case Horde_ActiveSync_Wbxml::EXT_1:
406: case Horde_ActiveSync_Wbxml::EXT_2:
407: continue;
408:
409: case Horde_ActiveSync_Wbxml::OPAQUE:
410: $length = $this->_getMBUInt();
411: $attr .= $this->_getOpaque($length);
412: return $element;
413:
414: case Horde_ActiveSync_Wbxml::LITERAL_AC:
415: return false;
416:
417: default:
418: if ($byte < 128) {
419: if ($attr != '') {
420: $attributes += $this->_splitAttribute($attr);
421: $attr = '';
422: }
423: }
424:
425: $attr .= $this->_getMapping($this->_attrcp, $byte);
426: break;
427: }
428: }
429: }
430:
431: 432: 433: 434: 435: 436: 437:
438: private function _splitAttribute($attr)
439: {
440: $attributes = array();
441: $pos = strpos($attr,chr(61));
442: if ($pos) {
443: $attributes[substr($attr, 0, $pos)] = substr($attr, $pos+1);
444: } else {
445: $attributes[$attr] = null;
446: }
447:
448: return $attributes;
449: }
450:
451: 452: 453: 454: 455:
456: private function _getTermStr()
457: {
458: $str = '';
459: while(1) {
460: $in = $this->_getByte();
461:
462: if ($in == 0) {
463: break;
464: } else {
465: $str .= chr($in);
466: }
467: }
468:
469: return $str;
470: }
471:
472: 473: 474: 475: 476: 477: 478:
479: private function _getOpaque($len)
480: {
481: return fread($this->_stream, $len);
482: }
483:
484: 485: 486: 487: 488:
489: private function _getByte()
490: {
491: $ch = fread($this->_stream, 1);
492: if (strlen($ch) > 0) {
493: $ch = ord($ch);
494:
495: return $ch;
496: } else {
497: return;
498: }
499: }
500:
501: 502: 503: 504: 505:
506: private function _getMBUInt()
507: {
508: $uint = 0;
509: while (1) {
510: $byte = $this->_getByte();
511: $uint |= $byte & 0x7f;
512: if ($byte & 0x80) {
513: $uint = $uint << 7;
514: } else {
515: break;
516: }
517: }
518:
519: return $uint;
520: }
521:
522: 523: 524: 525: 526:
527: private function _getStringTable()
528: {
529: $stringtable = '';
530: $length = $this->_getMBUInt();
531: if ($length > 0) {
532: $stringtable = fread($this->_stream, $length);
533: }
534:
535: return $stringtable;
536: }
537:
538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548:
549: private function _getStringTableEntry($id)
550: {
551: throw new Horde_ActiveSync_Exception('Not implemented');
552: }
553:
554: 555: 556: 557: 558: 559: 560: 561:
562: private function _getMapping($cp, $id)
563: {
564: if (!isset($this->_dtd['codes'][$cp]) || !isset($this->_dtd['codes'][$cp][$id])) {
565: return false;
566: } else {
567: if (isset($this->_dtd['namespaces'][$cp])) {
568: return $this->_dtd['namespaces'][$cp] . ':' . $this->_dtd['codes'][$cp][$id];
569: } else {
570: return $this->_dtd['codes'][$cp][$id];
571: }
572: }
573: }
574:
575: }