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: class Horde_ActiveSync_Wbxml_Encoder extends Horde_ActiveSync_Wbxml
25: {
26: 27: 28: 29: 30: 31: 32: 33:
34: private $_stack = array();
35:
36: 37: 38: 39: 40: 41: 42:
43: function __construct($output)
44: {
45: parent::__construct($output);
46:
47:
48: $dtd = array();
49: foreach ($this->_dtd['namespaces'] as $nsid => $nsname) {
50: $dtd['namespaces'][$nsname] = $nsid;
51: }
52:
53: foreach ($this->_dtd['codes'] as $cp => $value) {
54: $dtd['codes'][$cp] = array();
55: foreach ($this->_dtd['codes'][$cp] as $tagid => $tagname) {
56: $dtd['codes'][$cp][$tagname] = $tagid;
57: }
58: }
59: $this->_dtd = $dtd;
60: }
61:
62: 63: 64: 65: 66:
67: public function setLogger(Horde_Log_Logger $logger)
68: {
69: $this->_logger = $logger;
70: }
71:
72: 73: 74: 75: 76:
77: public function startWBXML()
78: {
79: header('Content-Type: application/vnd.ms-sync.wbxml');
80: $this->outputWbxmlHeader();
81: }
82:
83: public function ()
84: {
85: $this->_outByte(0x03);
86: $this->_outMBUInt(0x01);
87: $this->_outMBUInt(106);
88: $this->_outMBUInt(0x00);
89: }
90:
91: 92: 93: 94: 95: 96: 97: 98: 99:
100: public function startTag($tag, $attributes = false, $output_empty = false)
101: {
102: $stackelem = array();
103: if (!$output_empty) {
104: $stackelem['tag'] = $tag;
105: $stackelem['attributes'] = $attributes;
106: $stackelem['nocontent'] = $output_empty;
107: $stackelem['sent'] = false;
108: array_push($this->_stack, $stackelem);
109: } else {
110:
111: $this->_outputStack();
112: $this->_startTag($tag, $attributes, $output_empty);
113: }
114: }
115:
116: 117: 118: 119: 120:
121: public function endTag()
122: {
123: $stackelem = array_pop($this->_stack);
124:
125: if ($stackelem['sent']) {
126: $this->_endTag();
127: }
128: }
129:
130: 131: 132: 133: 134: 135: 136:
137: public function content($content)
138: {
139: 140:
141: $content = str_replace('\0', '', $content);
142: if ('x' . $content == 'x') {
143: return;
144: }
145: $this->_outputStack();
146: $this->_content($content);
147: }
148:
149: 150: 151: 152: 153:
154: private function _outputStack()
155: {
156: for ($i=0; $i < count($this->_stack); $i++) {
157: if (!$this->_stack[$i]['sent']) {
158: $this->_startTag($this->_stack[$i]['tag'], $this->_stack[$i]['attributes'], $this->_stack[$i]['nocontent']);
159: $this->_stack[$i]['sent'] = true;
160: }
161: }
162: }
163:
164: 165: 166: 167: 168: 169: 170: 171: 172:
173: private function _startTag($tag, $attributes = false, $output_empty = false)
174: {
175: $this->_logStartTag($tag, $attributes, $output_empty);
176: $mapping = $this->_getMapping($tag);
177: if (!$mapping) {
178: return false;
179: }
180:
181:
182: if ($this->_tagcp != $mapping['cp']) {
183: $this->_outSwitchPage($mapping['cp']);
184: $this->_tagcp = $mapping['cp'];
185: }
186:
187:
188: $code = $mapping['code'];
189: if (isset($attributes) && is_array($attributes) && count($attributes) > 0) {
190: $code |= 0x80;
191: } elseif (!isset($output_empty) || !$output_empty) {
192: $code |= 0x40;
193: }
194: $this->_outByte($code);
195: if ($code & 0x80) {
196: $this->_outAttributes($attributes);
197: }
198: }
199:
200: 201: 202: 203: 204: 205: 206:
207: private function _content($content)
208: {
209: $this->_logContent($content);
210: $this->_outByte(Horde_ActiveSync_Wbxml::STR_I);
211: $this->_outTermStr($content);
212: }
213:
214: 215: 216: 217: 218:
219: function _endTag() {
220: $this->_logEndTag();
221: $this->_outByte(Horde_ActiveSync_Wbxml::END);
222: }
223:
224: 225: 226: 227: 228: 229:
230: private function _outByte($byte)
231: {
232: fwrite($this->_stream, chr($byte));
233: }
234:
235: 236: 237: 238: 239:
240: private function _outMBUInt($uint)
241: {
242: while (1) {
243: $byte = $uint & 0x7f;
244: $uint = $uint >> 7;
245: if ($uint == 0) {
246: $this->_outByte($byte);
247: break;
248: } else {
249: $this->_outByte($byte | 0x80);
250: }
251: }
252: }
253:
254: 255: 256: 257: 258: 259: 260:
261: private function _outTermStr($content)
262: {
263: fwrite($this->_stream, $content);
264: fwrite($this->_stream, chr(0));
265: }
266:
267: 268: 269: 270: 271:
272: private function _outAttributes()
273: {
274:
275:
276:
277:
278: $this->_outByte(Horde_ActiveSync_Wbxml::END);
279: }
280:
281: 282: 283: 284: 285:
286: private function _outSwitchPage($page)
287: {
288: $this->_outByte(Horde_ActiveSync_Wbxml::SWITCH_PAGE);
289: $this->_outByte($page);
290: }
291:
292: 293: 294: 295: 296: 297: 298:
299: private function _getMapping($tag)
300: {
301: $mapping = array();
302: $split = $this->_splitTag($tag);
303: if (isset($split['ns'])) {
304: $cp = $this->_dtd['namespaces'][$split['ns']];
305: } else {
306: $cp = 0;
307: }
308:
309: $code = $this->_dtd['codes'][$cp][$split['tag']];
310: $mapping['cp'] = $cp;
311: $mapping['code'] = $code;
312:
313: return $mapping;
314: }
315:
316: 317: 318: 319: 320: 321: 322: 323:
324: private function _splitTag($fulltag)
325: {
326: $ns = false;
327: $pos = strpos($fulltag, chr(58));
328: if ($pos) {
329: $ns = substr($fulltag, 0, $pos);
330: $tag = substr($fulltag, $pos+1);
331: } else {
332: $tag = $fulltag;
333: }
334:
335: $ret = array();
336: if ($ns) {
337: $ret['ns'] = $ns;
338: }
339: $ret['tag'] = $tag;
340:
341: return $ret;
342: }
343:
344: 345: 346: 347: 348: 349: 350: 351: 352:
353: private function _logStartTag($tag, $attr, $output_empty)
354: {
355: $spaces = str_repeat(' ', count($this->_logStack));
356: if ($output_empty) {
357: $this->_logger->debug(sprintf('O %s <%s/>', $spaces, $tag));
358: } else {
359: array_push($this->_logStack, $tag);
360: $this->_logger->debug(sprintf('O %s <%s>', $spaces, $tag));
361: }
362: }
363:
364: 365: 366: 367: 368:
369: private function _logEndTag()
370: {
371: $spaces = str_repeat(' ', count($this->_logStack) - 1);
372: $tag = array_pop($this->_logStack);
373: $this->_logger->debug(sprintf('O %s <%s/>', $spaces, $tag));
374: }
375:
376: 377: 378: 379: 380: 381: 382:
383: private function _logContent($content)
384: {
385: $spaces = str_repeat(' ', count($this->_logStack) + 1);
386: $this->_logger->debug('O ' . $spaces . $content);
387: }
388:
389: }