"Fossies" - the Fresh Open Source Software Archive

Member "z-push/wbxml.php" (2 Aug 2013, 24477 Bytes) of package /linux/www/old/group-e_z-push_v3.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "wbxml.php" see the Fossies "Dox" file reference documentation.

    1 <?php
    2 /***********************************************
    3 * File      :   wbxml.php
    4 * Project   :   Z-Push
    5 * Descr     :   WBXML mapping file
    6 *
    7 * Created   :   01.10.2007
    8 *
    9 * � Zarafa Deutschland GmbH, www.zarafaserver.de
   10 * This file is distributed under GPL v2.
   11 * Consult LICENSE file for details
   12 ************************************************/
   13 include_once('debug.php');
   14 
   15 define('WBXML_DEBUG', false);
   16 
   17 define('WBXML_SWITCH_PAGE',     0x00);
   18 define('WBXML_END',             0x01);
   19 define('WBXML_ENTITY',          0x02);
   20 define('WBXML_STR_I',           0x03);
   21 define('WBXML_LITERAL',         0x04);
   22 define('WBXML_EXT_I_0',         0x40);
   23 define('WBXML_EXT_I_1',         0x41);
   24 define('WBXML_EXT_I_2',         0x42);
   25 define('WBXML_PI',              0x43);
   26 define('WBXML_LITERAL_C',       0x44);
   27 define('WBXML_EXT_T_0',         0x80);
   28 define('WBXML_EXT_T_1',         0x81);
   29 define('WBXML_EXT_T_2',         0x82);
   30 define('WBXML_STR_T',           0x83);
   31 define('WBXML_LITERAL_A',       0x84);
   32 define('WBXML_EXT_0',           0xC0);
   33 define('WBXML_EXT_1',           0xC1);
   34 define('WBXML_EXT_2',           0xC2);
   35 define('WBXML_OPAQUE',          0xC3);
   36 define('WBXML_LITERAL_AC',      0xC4);
   37 
   38 define('EN_TYPE',               1);
   39 define('EN_TAG',                2);
   40 define('EN_CONTENT',            3);
   41 define('EN_FLAGS',              4);
   42 define('EN_ATTRIBUTES',         5);
   43 
   44 define('EN_TYPE_STARTTAG',      1);
   45 define('EN_TYPE_ENDTAG',        2);
   46 define('EN_TYPE_CONTENT',       3);
   47 
   48 define('EN_FLAGS_CONTENT',      1);
   49 define('EN_FLAGS_ATTRIBUTES',   2);
   50 
   51 class WBXMLDecoder {
   52     var $dtd;
   53     var $in;
   54 
   55     var $version;
   56     var $publicid;
   57     var $publicstringid;
   58     var $charsetid;
   59     var $stringtable;
   60 
   61     var $tagcp = 0;
   62     var $attrcp = 0;
   63 
   64     var $ungetbuffer;
   65 
   66     var $logStack = array();
   67     var $_inputRaw = '';
   68 
   69     function WBXMLDecoder($input, $dtd) {
   70         $this->in = $input;
   71         $this->dtd = $dtd;
   72 
   73         $this->version = $this->getByte();
   74         $this->publicid = $this->getMBUInt();
   75         if($this->publicid == 0) {
   76             $this->publicstringid = $this->getMBUInt();
   77         }
   78 
   79         $this->charsetid = $this->getMBUInt();
   80         $this->stringtable = $this->getStringTable();
   81 
   82     }
   83 
   84     // Returns either start, content or end, and auto-concatenates successive content
   85     function getElement()
   86     {
   87         $element = $this->getToken();
   88 
   89         switch($element[EN_TYPE]) {
   90             case EN_TYPE_STARTTAG:
   91                 return $element;
   92             case EN_TYPE_ENDTAG:
   93                 return $element;
   94             case EN_TYPE_CONTENT:
   95                 while(1) {
   96                     $next = $this->getToken();
   97                     if($next == false)
   98                         return false;
   99                     else if($next[EN_TYPE] == EN_CONTENT) {
  100                         $element[EN_CONTENT] .= $next[EN_CONTENT];
  101                     } else {
  102                         $this->ungetElement($next);
  103                         break;
  104                     }
  105                 }
  106                 return $element;
  107         }
  108 
  109         return false;
  110     }
  111 
  112     function peek()
  113     {
  114         $element = $this->getElement();
  115 
  116         $this->ungetElement($element);
  117 
  118         return $element;
  119     }
  120 
  121     function getElementStartTag($tag)
  122     {
  123         $element = $this->getToken();
  124 
  125 //  debugLog("WBXML Element getElementStartTag ".print_r($element,true));
  126         if($element[EN_TYPE] == EN_TYPE_STARTTAG && $element[EN_TAG] == $tag)
  127             return $element;
  128         else {
  129             debug("Unmatched tag $tag:");
  130             debug(print_r($element,true));
  131             $this->ungetElement($element);
  132         }
  133 
  134         return false;
  135     }
  136 
  137     function getElementEndTag()
  138     {
  139         $element = $this->getToken();
  140 
  141 //  debugLog("WBXML Element getElementEndTag ".print_r($element,true));
  142         if($element[EN_TYPE] == EN_TYPE_ENDTAG)
  143             return $element;
  144         else {
  145             debug("Unmatched end tag:");
  146             if (defined('WBXML_DEBUG') &&
  147                 WBXML_DEBUG == true) {
  148                 debugLog("WBXML RAW Input (bin2hex): ".bin2hex($this->_inputRaw));
  149             }
  150             debug(print_r($element,true));
  151             $bt = debug_backtrace();
  152             $c = count($bt);
  153             debugLog(print_r($bt,true));
  154             debug("From " . $bt[$c-2]["file"] . ":" . $bt[$c-2]["line"]);
  155             $this->ungetElement($element);
  156         }
  157 
  158         return false;
  159     }
  160 
  161     function getElementContent()
  162     {
  163         $element = $this->getToken();
  164 
  165 //  debugLog("WBXML Element getElementContent ".print_r($element,true));
  166         if($element[EN_TYPE] == EN_TYPE_CONTENT) {
  167             return $element[EN_CONTENT];
  168         } 
  169         // also allow empty tags
  170 //        else if($element[EN_TYPE] == EN_TYPE_ENDTAG) {
  171 //      debugLog("WBXML Element EN_TYPE_ENDTAG ".print_r($element,true));
  172 //            $this->ungetElement($element);
  173 //            return "";
  174 //        }
  175         else {
  176             debug("Unmatched content:");
  177             debug(print_r($element, true));
  178             $this->ungetElement($element);
  179         }
  180 
  181         return false;
  182     }
  183 
  184     // ---------------------- Private functions ------------------------
  185 
  186     function getToken() {
  187         // See if there's something in the ungetBuffer
  188         if($this->ungetbuffer) {
  189             $element = $this->ungetbuffer;
  190             $this->ungetbuffer = false;
  191             return $element;
  192         }
  193 
  194         $el = $this->_getToken();
  195 
  196         $this->logToken($el);
  197 
  198         return $el;
  199     }
  200 
  201     function logToken($el) {
  202         if(!WBXML_DEBUG)
  203             return;
  204         $spaces = str_repeat(" ", count($this->logStack));
  205 
  206         switch($el[EN_TYPE]) {
  207             case EN_TYPE_STARTTAG:
  208                 if($el[EN_FLAGS] & EN_FLAGS_CONTENT) {
  209                     debugLog("I " . $spaces . " <". $el[EN_TAG] . ">");
  210                     array_push($this->logStack, $el[EN_TAG]);
  211                 } else
  212                     debugLog("I " . $spaces . " <" . $el[EN_TAG] . "/>");
  213 
  214                 break;
  215             case EN_TYPE_ENDTAG:
  216                 $tag = array_pop($this->logStack);
  217                 debugLog("I " . $spaces . "</" . $tag . ">");
  218                 break;
  219             case EN_TYPE_CONTENT:
  220                 debugLog("I " . $spaces . " " . $el[EN_CONTENT]);
  221                 break;
  222         default:
  223                 debugLog("I " . $spaces . " " . $el[EN_TYPE]);
  224         }
  225     }
  226 
  227     // Returns either a start tag, content or end tag
  228     function _getToken() {
  229 
  230         // Get the data from the input stream
  231         $element = array();
  232 
  233         while(1) {
  234             $byte = $this->getByte();
  235             if(!isset($byte))
  236                 break;
  237 
  238 //      debugLog("Byte: ".ord($byte));
  239 
  240             switch($byte) {
  241                 case WBXML_SWITCH_PAGE:
  242                     $this->tagcp = $this->getByte();
  243                     continue;
  244 
  245                 case WBXML_END:
  246                     $element[EN_TYPE] = EN_TYPE_ENDTAG;
  247                     return $element;
  248 
  249                 case WBXML_ENTITY:
  250                     $entity = $this->getMBUInt();
  251                     $element[EN_TYPE] = EN_TYPE_CONTENT;
  252                     $element[EN_CONTENT] = $this->entityToCharset($entity);
  253                     return $element;
  254 
  255                 case WBXML_STR_I:
  256                     $element[EN_TYPE] = EN_TYPE_CONTENT;
  257                     $element[EN_CONTENT] = $this->getTermStr();
  258                     return $element;
  259 
  260                 case WBXML_LITERAL:
  261                     $element[EN_TYPE] = EN_TYPE_STARTTAG;
  262                     $element[EN_TAG] = $this->getStringTableEntry($this->getMBUInt());
  263                     $element[EN_FLAGS] = 0;
  264                     return $element;
  265 
  266                 case WBXML_EXT_I_0:
  267                 case WBXML_EXT_I_1:
  268                 case WBXML_EXT_I_2:
  269                     $this->getTermStr();
  270                     // Ignore extensions
  271                     continue;
  272 
  273                 case WBXML_PI:
  274                     // Ignore PI
  275                     $this->getAttributes();
  276                     continue;
  277 
  278                 case WBXML_LITERAL_C:
  279                     $element[EN_TYPE] = EN_TYPE_STARTTAG;
  280                     $element[EN_TAG] = $this->getStringTableEntry($this->getMBUInt());
  281                     $element[EN_FLAGS] = EN_FLAGS_CONTENT;
  282                     return $element;
  283 
  284                 case WBXML_EXT_T_0:
  285                 case WBXML_EXT_T_1:
  286                 case WBXML_EXT_T_2:
  287                     $this->getMBUInt();
  288                     // Ingore extensions;
  289                     continue;
  290 
  291                 case WBXML_STR_T:
  292                     $element[EN_TYPE] = EN_TYPE_CONTENT;
  293                     $element[EN_CONTENT] = $this->getStringTableEntry($this->getMBUInt());
  294                     return $element;
  295 
  296                 case WBXML_LITERAL_A:
  297                     $element[EN_TYPE] = EN_TYPE_STARTTAG;
  298                     $element[EN_TAG] = $this->getStringTableEntry($this->getMBUInt());
  299                     $element[EN_ATTRIBUTES] = $this->getAttributes();
  300                     $element[EN_FLAGS] = EN_FLAGS_ATTRIBUTES;
  301                     return $element;
  302                 case WBXML_EXT_0:
  303                 case WBXML_EXT_1:
  304                 case WBXML_EXT_2:
  305                     continue;
  306 
  307                 case WBXML_OPAQUE:
  308                     $length = $this->getMBUInt();
  309                     $element[EN_TYPE] = EN_TYPE_CONTENT;
  310                     $element[EN_CONTENT] = $this->getOpaque($length);
  311                     return $element;
  312 
  313                 case WBXML_LITERAL_AC:
  314                     $element[EN_TYPE] = EN_TYPE_STARTTAG;
  315                     $element[EN_TAG] = $this->getStringTableEntry($this->getMBUInt());
  316                     $element[EN_ATTRIBUTES] = $this->getAttributes();
  317                     $element[EN_FLAGS] = EN_FLAGS_ATTRIBUTES | EN_FLAGS_CONTENT;
  318                     return $element;
  319 
  320                 default:
  321                     $element[EN_TYPE] = EN_TYPE_STARTTAG;
  322                     $element[EN_TAG] = $this->getMapping($this->tagcp, $byte & 0x3f);
  323                     $element[EN_FLAGS] = ($byte & 0x80 ? EN_FLAGS_ATTRIBUTES : 0) | ($byte & 0x40 ? EN_FLAGS_CONTENT : 0);
  324                     if($byte & 0x80)
  325                         $element[EN_ATTRIBUTES] = $this->getAttributes();
  326                     return $element;
  327             }
  328         }
  329     }
  330 
  331     function ungetElement($element) {
  332         if($this->ungetbuffer)
  333             debugLog("Double unget!");
  334 
  335         $this->ungetbuffer = $element;
  336     }
  337 
  338     function getAttributes() {
  339         $attributes = array();
  340         $attr = "";
  341 
  342         while(1) {
  343             $byte = $this->getByte();
  344 
  345             if(count($byte) == 0)
  346                 break;
  347 
  348             switch($byte) {
  349                 case WBXML_SWITCH_PAGE:
  350                     $this->attrcp = $this->getByte();
  351                     break;
  352 
  353                 case WBXML_END:
  354                     if($attr != "")
  355                         $attributes += $this->splitAttribute($attr);
  356 
  357                     return $attributes;
  358 
  359                 case WBXML_ENTITY:
  360                     $entity = $this->getMBUInt();
  361                     $attr .= $this->entityToCharset($entity);
  362                     return $element;
  363 
  364                 case WBXML_STR_I:
  365                     $attr .= $this->getTermStr();
  366                     return $element;
  367 
  368                 case WBXML_LITERAL:
  369                     if($attr != "")
  370                         $attributes += $this->splitAttribute($attr);
  371 
  372                     $attr = $this->getStringTableEntry($this->getMBUInt());
  373                     return $element;
  374 
  375                 case WBXML_EXT_I_0:
  376                 case WBXML_EXT_I_1:
  377                 case WBXML_EXT_I_2:
  378                     $this->getTermStr();
  379                     continue;
  380 
  381                 case WBXML_PI:
  382                 case WBXML_LITERAL_C:
  383                     // Invalid
  384                     return false;
  385 
  386                 case WBXML_EXT_T_0:
  387                 case WBXML_EXT_T_1:
  388                 case WBXML_EXT_T_2:
  389                     $this->getMBUInt();
  390                     continue;
  391 
  392                 case WBXML_STR_T:
  393                     $attr .= $this->getStringTableEntry($this->getMBUInt());
  394                     return $element;
  395 
  396                 case WBXML_LITERAL_A:
  397                     return false;
  398 
  399                 case WBXML_EXT_0:
  400                 case WBXML_EXT_1:
  401                 case WBXML_EXT_2:
  402                     continue;
  403 
  404                 case WBXML_OPAQUE:
  405                     $length = $this->getMBUInt();
  406                     $attr .= $this->getOpaque($length);
  407                     return $element;
  408 
  409                 case WBXML_LITERAL_AC:
  410                     return false;
  411 
  412                 default:
  413                     if($byte < 128) {
  414                         if($attr != "") {
  415                             $attributes += $this->splitAttribute($attr);
  416                             $attr = "";
  417                         }
  418                     }
  419 
  420                     $attr .= $this->getMapping($this->attrcp, $byte);
  421                     break;
  422             }
  423         }
  424 
  425     }
  426 
  427     function splitAttribute($attr) {
  428         $attributes = array();
  429 
  430         $pos = strpos($attr,chr(61)); // equals sign
  431 
  432         if($pos)
  433             $attributes[byte_substr($attr, 0, $pos)] = byte_substr($attr, $pos+1);
  434         else
  435             $attributes[$attr] = null;
  436 
  437         return $attributes;
  438     }
  439 
  440     function getTermStr() {
  441         $str = "";
  442         while(1) {
  443             $in = $this->getByte();
  444 
  445             if($in == 0)
  446                 break;
  447             else
  448                 $str .= chr($in);
  449         }
  450 
  451         return $str;
  452     }
  453 
  454     function getOpaque($len) {
  455         $result = "";
  456         while (byte_strlen($result) < $len) {
  457            if (($ch = fread($this->in, $len-byte_strlen($result))) == false) return false;
  458            $result .= $ch;
  459         };
  460         $this->_inputRaw .= $result;
  461         return $result;
  462     }
  463 
  464     function getByte() {
  465 
  466 //        $ch = fread($this->in, 1);
  467 // Start to cover timeout scenarios in input stream where stream is not eof but no char is read
  468 // Could solve t-sync problem. if not above line was replaced until END
  469         $i = 0;
  470         do {
  471             if ($i > 0) usleep(500);
  472             $ch = fread($this->in, 1);
  473             $i++;
  474         } while (!feof($this->in) &&
  475                   (byte_strlen($ch) == 0 && $i < 10));
  476         if ($i == 10 &&
  477             byte_strlen($ch) == 0 &&
  478             !feof($this->in)) {
  479             debugLog("wbxml: input stream is not feof and limit 10 reached during read without getting any byte from input!");
  480         }
  481 // END
  482         $this->_inputRaw .= $ch;
  483         if(byte_strlen($ch) > 0)
  484             return ord($ch);
  485         else
  486             return;
  487     }
  488 
  489     function getMBUInt() {
  490         $uint = 0;
  491 
  492         while(1) {
  493           $byte = $this->getByte();
  494 
  495           $uint |= $byte & 0x7f;
  496 
  497           if($byte & 0x80)
  498               $uint = $uint << 7;
  499           else
  500               break;
  501         }
  502 
  503         return $uint;
  504     }
  505 
  506     function getStringTable() {
  507         $stringtable = "";
  508 
  509         $length = $this->getMBUInt();
  510         if($length > 0)
  511             $stringtable = fread($this->in, $length);
  512 
  513         $this->_inputRaw .= $stringtable;
  514         return $stringtable;
  515     }
  516 
  517     function getMapping($cp, $id) {
  518         if(!isset($this->dtd["codes"][$cp]) || !isset($this->dtd["codes"][$cp][$id]))
  519             return false;
  520         else {
  521             if(isset($this->dtd["namespaces"][$cp])) {
  522                 return $this->dtd["namespaces"][$cp] . ":" . $this->dtd["codes"][$cp][$id];
  523             } else
  524                 return $this->dtd["codes"][$cp][$id];
  525         }
  526     }
  527 }
  528 
  529 class WBXMLEncoder {
  530     var $_dtd;
  531     var $_out;
  532 
  533     var $_tagcp;
  534     var $_attrcp;
  535 
  536     // ADDED dw2412 to support multipart output
  537     var $_multipart; // in case we need to export as multipart i.e. ITEM_OPERATIONS
  538 
  539     var $logStack = array();
  540 
  541     // We use a delayed output mechanism in which we only output a tag when it actually has something
  542     // in it. This can cause entire XML trees to disappear if they don't have output data in them; Ie
  543     // calling 'startTag' 10 times, and then 'endTag' will cause 0 bytes of output apart from the header.
  544 
  545     // Only when content() is called do we output the current stack of tags
  546 
  547     var $_stack;
  548 
  549     function WBXMLEncoder($output, $dtd) {
  550         $this->_out = $output;
  551 
  552         $this->_tagcp = 0;
  553         $this->_attrcp = 0;
  554 
  555         // reverse-map the DTD
  556         foreach($dtd["namespaces"] as $nsid => $nsname) {
  557             $this->_dtd["namespaces"][$nsname] = $nsid;
  558         }
  559 
  560         foreach($dtd["codes"] as $cp => $value) {
  561             $this->_dtd["codes"][$cp] = array();
  562             foreach($dtd["codes"][$cp] as $tagid => $tagname) {
  563                 $this->_dtd["codes"][$cp][$tagname] = $tagid;
  564             }
  565         }
  566         $this->_stack = array();
  567     }
  568 
  569     function startWBXML($multipart=false) {
  570         // START CHANGED dw2412 to support multipart output
  571         $this->_multipart=$multipart;
  572         if ($this->_multipart==true) {
  573             header("Content-Type: application/vnd.ms-sync.multipart");
  574         } else {
  575             header("Content-Type: application/vnd.ms-sync.wbxml");
  576         }
  577         // END CHANGED dw2412 to support multipart output
  578 
  579         $this->outByte(0x03); // WBXML 1.3
  580         $this->outMBUInt(0x01); // Public ID 1
  581         $this->outMBUInt(106); // UTF-8
  582         $this->outMBUInt(0x00); // string table length (0)
  583     }
  584 
  585     function startTag($tag, $attributes = false, $nocontent = false) {
  586         $stackelem = array();
  587 
  588         if(!$nocontent) {
  589             $stackelem['tag'] = $tag;
  590             $stackelem['attributes'] = $attributes;
  591             $stackelem['nocontent'] = $nocontent;
  592             $stackelem['sent'] = false;
  593 
  594             array_push($this->_stack, $stackelem);
  595 
  596             // If 'nocontent' is specified, then apparently the user wants to force
  597             // output of an empty tag, and we therefore output the stack here
  598         } else {
  599             $this->_outputStack();
  600             $this->_startTag($tag, $attributes, $nocontent);
  601         }
  602     }
  603 
  604     function endTag() {
  605         $stackelem = array_pop($this->_stack);
  606 
  607         // Only output end tags for items that have had a start tag sent
  608         if($stackelem['sent']) {
  609             $this->_endTag();
  610             // START ADDED dw2412 multipart output handling
  611             if(sizeof($this->_stack)==0 && $this->_multipart==true) {
  612                 // NOT THE NICE WAY IMHO but this keeps existing logic of data output in index.php file...
  613                 // first we grab the existing wbxml output, manipulate it and write it buffered way back.
  614                 $len = ob_get_length();
  615                 $data = ob_get_contents();
  616                 ob_end_clean();
  617                 ob_start();
  618                 $blockstart = ((sizeof($this->_bodyparts)+1)*2)*4+4;
  619                 $sizeinfo = pack("iii",sizeof($this->_bodyparts)+1,$blockstart,$len);
  620                 debugLog("GZip compressed Multipart Debug Output Total parts " . (sizeof($this->_bodyparts)+1));
  621                 debugLog(sprintf("Datapart BlockStart: %d Len: %d Content: %s",$blockstart,$len,bin2hex($data)));
  622                 foreach($this->_bodyparts as $bp) {
  623                     $blockstart = $blockstart + $len;
  624                     $len = byte_strlen(bin2hex($bp))/ 2;
  625                     $sizeinfo .= pack("ii",$blockstart,$len);
  626                     debugLog(sprintf("Bodypart BlockStart: %d Len: %d Content: %s",$blockstart,$len,bin2hex($bp)));
  627                 }
  628                 fwrite($this->_out,$sizeinfo);
  629                 fwrite($this->_out,$data);
  630                 foreach($this->_bodyparts as $bp) {
  631                     fwrite($this->_out,$bp);
  632                 }
  633             }
  634             // END ADDED dw2412 multipart output handling
  635         }
  636     }
  637 
  638     function content($content) {
  639         // We need to filter out any \0 chars because it's the string terminator in WBXML. We currently
  640         // cannot send \0 characters within the XML content anywhere.
  641         $content = str_replace("\0","",$content);
  642 
  643         if("x" . $content == "x")
  644             return;
  645         $this->_outputStack();
  646         $this->_content($content);
  647     }
  648 
  649     function contentopaque($content) {
  650         if("x" . $content == "x")
  651             return;
  652         $this->_outputStack();
  653         $this->_contentopaque($content);
  654     }
  655 
  656     // Output any tags on the stack that haven't been output yet
  657     function _outputStack() {
  658         for($i=0;$i<count($this->_stack);$i++) {
  659             if(!$this->_stack[$i]['sent']) {
  660                 $this->_startTag($this->_stack[$i]['tag'], $this->_stack[$i]['attributes'], $this->_stack[$i]['nocontent']);
  661                 $this->_stack[$i]['sent'] = true;
  662             }
  663         }
  664     }
  665 
  666     // Outputs an actual start tag
  667     function _startTag($tag, $attributes = false, $nocontent = false) {
  668         $this->logStartTag($tag, $attributes, $nocontent);
  669 
  670         $mapping = $this->getMapping($tag);
  671 
  672         if(!$mapping)
  673             return false;
  674 
  675         if($this->_tagcp != $mapping["cp"]) {
  676             $this->outSwitchPage($mapping["cp"]);
  677             $this->_tagcp = $mapping["cp"];
  678         }
  679 
  680         $code = $mapping["code"];
  681         if(isset($attributes) && is_array($attributes) && count($attributes) > 0) {
  682             $code |= 0x80;
  683         }
  684 
  685         if(!isset($nocontent) || !$nocontent)
  686             $code |= 0x40;
  687 
  688         $this->outByte($code);
  689 
  690         if($code & 0x80)
  691             $this->outAttributes($attributes);
  692     }
  693 
  694     // Outputs actual data
  695     function _content($content) {
  696         $this->logContent($content);
  697         $this->outByte(WBXML_STR_I);
  698         $this->outTermStr($content);
  699     }
  700 
  701     // Outputs actual data
  702     function _contentopaque($content) {
  703         $this->logContent("OPAQUE: ".bin2hex($content));
  704         $this->outByte(WBXML_OPAQUE);
  705         $this->outByte(byte_strlen($content));
  706         $this->outOpaque($content);
  707     }
  708 
  709     // Outputs an actual end tag
  710     function _endTag() {
  711         $this->logEndTag();
  712         $this->outByte(WBXML_END);
  713     }
  714 
  715     // --------------------------- Private
  716 
  717     function outByte($byte) {
  718         fwrite($this->_out, chr($byte));
  719     }
  720 
  721     function outMBUInt($uint) {
  722         while(1) {
  723             $byte = $uint & 0x7f;
  724             $uint = $uint >> 7;
  725             if($uint == 0) {
  726                 $this->outByte($byte);
  727                 break;
  728             } else {
  729                 $this->outByte($byte | 0x80);
  730             }
  731         }
  732     }
  733 
  734     function outTermStr($content) {
  735         fwrite($this->_out, $content);
  736         fwrite($this->_out, chr(0));
  737     }
  738 
  739     function outOpaque($content) {
  740         fwrite($this->_out, $content);
  741     }
  742 
  743     function outAttributes() {
  744         // We don't actually support this, because to do so, we would have
  745         // to build a string table before sending the data (but we can't
  746         // because we're streaming), so we'll just send an END, which just
  747         // terminates the attribute list with 0 attributes.
  748         $this->outByte(WBXML_END);
  749     }
  750 
  751     function outSwitchPage($page) {
  752         $this->outByte(WBXML_SWITCH_PAGE);
  753         $this->outByte($page);
  754     }
  755 
  756     function getMapping($tag) {
  757         $mapping = array();
  758 
  759         $split = $this->splitTag($tag);
  760 
  761         if(isset($split["ns"])) {
  762             $cp = $this->_dtd["namespaces"][$split["ns"]];
  763         } else {
  764             $cp = 0;
  765         }
  766 
  767         $code = $this->_dtd["codes"][$cp][$split["tag"]];
  768 
  769         $mapping["cp"] = $cp;
  770         $mapping["code"] = $code;
  771 
  772         return $mapping;
  773     }
  774 
  775     function splitTag($fulltag) {
  776         $ns = false;
  777         $pos = strpos($fulltag, chr(58)); // chr(58) == ':'
  778 
  779         if($pos) {
  780             $ns = byte_substr($fulltag, 0, $pos);
  781             $tag = byte_substr($fulltag, $pos+1);
  782         } else {
  783             $tag = $fulltag;
  784         }
  785 
  786         $ret = array();
  787         if($ns)
  788             $ret["ns"] = $ns;
  789         $ret["tag"] = $tag;
  790 
  791         return $ret;
  792     }
  793 
  794     function logStartTag($tag, $attr, $nocontent) {
  795         if(!WBXML_DEBUG)
  796             return;
  797 
  798         $spaces = str_repeat(" ", count($this->logStack));
  799         if($nocontent)
  800             debugLog("O " . $spaces . " <$tag/>");
  801         else {
  802             array_push($this->logStack, $tag);
  803             debugLog("O " . $spaces . " <$tag>");
  804         }
  805     }
  806 
  807     function logEndTag() {
  808         if(!WBXML_DEBUG)
  809             return;
  810 
  811         $spaces = str_repeat(" ", count($this->logStack));
  812         $tag = array_pop($this->logStack);
  813         debugLog("O " . $spaces . "</$tag>");
  814     }
  815 
  816     function logContent($content) {
  817         if(!WBXML_DEBUG)
  818             return;
  819 
  820         $spaces = str_repeat(" ", count($this->logStack));
  821         debugLog("O " . $spaces . $content);
  822     }
  823 }