Overview

Packages

  • ActiveSync
  • None

Classes

  • Horde_ActiveSync
  • Horde_ActiveSync_Connector_Exporter
  • Horde_ActiveSync_Connector_Importer
  • Horde_ActiveSync_Driver_Base
  • Horde_ActiveSync_Exception
  • Horde_ActiveSync_Exception_InvalidRequest
  • Horde_ActiveSync_Exception_StateGone
  • Horde_ActiveSync_Message_Base
  • Horde_ActiveSync_Request_Base
  • Horde_ActiveSync_Request_FolderCreate
  • Horde_ActiveSync_Request_FolderSync
  • Horde_ActiveSync_Request_GetHierarchy
  • Horde_ActiveSync_Request_GetItemEstimate
  • Horde_ActiveSync_Request_MeetingResponse
  • Horde_ActiveSync_Request_MoveItems
  • Horde_ActiveSync_Request_Notify
  • Horde_ActiveSync_Request_Ping
  • Horde_ActiveSync_Request_Provision
  • Horde_ActiveSync_Request_Search
  • Horde_ActiveSync_Request_SendMail
  • Horde_ActiveSync_Request_SmartForward
  • Horde_ActiveSync_Request_SmartReply
  • Horde_ActiveSync_Request_Sync
  • Horde_ActiveSync_State_File
  • Horde_ActiveSync_Sync
  • Horde_ActiveSync_Wbxml
  • Horde_ActiveSync_Wbxml_Decoder
  • Horde_ActiveSync_Wbxml_Encoder
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * ActiveSync specific WBXML decoder.
  4:  *
  5:  * @author Michael J. Rubinsky <mrubinsk@horde.org>
  6:  * @package ActiveSync
  7:  */
  8: 
  9: /**
 10:  * File      :   wbxml.php
 11:  * Project   :   Z-Push
 12:  * Descr     :   WBXML mapping file
 13:  *
 14:  * Created   :   01.10.2007
 15:  *
 16:  * � Zarafa Deutschland GmbH, www.zarafaserver.de
 17:  * This file is distributed under GPL-2.0.
 18:  * Consult COPYING file for details
 19:  */
 20: class Horde_ActiveSync_Wbxml_Decoder extends Horde_ActiveSync_Wbxml
 21: {
 22:     // These seem to only be used in the Const'r, and I can't find any
 23:     // client code that access these properties...
 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:      * Start reading the wbxml stream, pulling off the initial header and
 35:      * populate the properties.
 36:      *
 37:      * @return void
 38:      */
 39:     public function readWbxmlHeader()
 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:      * Set the logger instance
 52:      *
 53:      * @param Horde_Log_Logger $logger  The logger.
 54:      */
 55:     public function setLogger(Horde_Log_Logger $logger)
 56:     {
 57:         $this->_logger = $logger;
 58:     }
 59: 
 60:     /**
 61:      * Returns either start, content or end, and auto-concatenates successive
 62:      * content
 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:      * @return array  The next element in the stream.
 94:      */
 95:     public function peek()
 96:     {
 97:         $element = $this->getElement();
 98:         $this->_ungetElement($element);
 99: 
100:         return $element;
101:     }
102: 
103:     /**
104:      * Get the next tag, which is assumed to be a start tag.
105:      *
106:      * @param string $tag  The element that this should be a start tag for.
107:      *
108:      * @return mixed  The start tag array | false on failure.
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:      * Get the next tag, which is assumed to be an end tag.
127:      *
128:      * @return mixed  The element array | false on failure.
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:      * Get the element contents
146:      *
147:      * @return mixed  The content of the current element | false on failure.
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:      * Get the next [start | content | end] tag.
165:      *
166:      * @return array  The next, complete, token array.
167:      */
168:     public function getToken()
169:     {
170:         // See if there's something in the ungetBuffer
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:      * Log the token.
185:      *
186:      * @param array  The element array.
187:      *
188:      * @return void
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:      * Get the next start tag, content or end tag
214:      *
215:      * @return array  The element array.
216:      */
217:    private function _getToken() {
218: 
219:         // Get the data from the input stream
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:                 // Ignore extensions
260:                 continue;
261: 
262:             case Horde_ActiveSync_Wbxml::PI:
263:                 // Ignore PI
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:                 // Ingore extensions;
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:      * Unget the specified element from the stream. Places the element into
323:      * the unget buffer.
324:      *
325:      * @param array $element  The element array to unget.
326:      *
327:      * @return void
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:      * Get the element attributes
339:      *
340:      * @return mixed  The value of the element's attributes.
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:                 // Invalid
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:      * Parses an attribute string
433:      *
434:      * @param string $attr  The raw attribute value.
435:      *
436:      * @return array  The attribute hash
437:      */
438:     private function _splitAttribute($attr)
439:     {
440:         $attributes = array();
441:         $pos = strpos($attr,chr(61)); // equals sign
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:      * Get a null terminated string from the stream.
453:      *
454:      * @return string  The string
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:      * Get an opaque value from the stream of the specified length.
474:      *
475:      * @param integer $len  The length of the data to fetch.
476:      *
477:      * @return string  A string of bytes representing the opaque value.
478:      */
479:     private function _getOpaque($len)
480:     {
481:         return fread($this->_stream, $len);
482:     }
483: 
484:     /**
485:      * Fetch a single byte from the stream.
486:      *
487:      * @return string  The single byte.
488:      */
489:     private function _getByte()
490:     {
491:         $ch = fread($this->_stream, 1);
492:         if (strlen($ch) > 0) {
493:             $ch = ord($ch);
494:             //$this->_logger->debug('_getByte: ' . $ch);
495:             return $ch;
496:         } else {
497:             return;
498:         }
499:     }
500: 
501:     /**
502:      * Get an MBU integer
503:      *
504:      * @return integer
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:      * Fetch the string table. Don't think we use the results anywhere though.
524:      *
525:      * @return string  The string table.
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:      * Really don't know for sure what this method is supposed to do, it is
540:      * called from numerous places in this class, but the original zpush code
541:      * did not contain this method...so, either it's completely broken, or
542:      * normal use-cases do not reach the calling code. Either way, it needs to
543:      * eventually be fixed.
544:      *
545:      * @param integer $id  The entry to return??
546:      *
547:      * @return string
548:      */
549:     private function _getStringTableEntry($id)
550:     {
551:         throw new Horde_ActiveSync_Exception('Not implemented');
552:     }
553: 
554:     /**
555:      * Get a dtd mapping
556:      *
557:      * @param integer $cp  The codepage to use.
558:      * @param integer $id  The property.
559:      *
560:      * @return mixed  The mapped value.
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: }
API documentation generated by ApiGen