Support Joomla!

Joomla! 1.5 Documentation

Packages

Package: Unknown

Developer Network License

The Joomla! Developer Network content is © copyright 2006 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution- NonCommercial- ShareAlike 2.5
Source code for file /phpxmlrpc/xmlrpc.php

Documentation is available at xmlrpc.php

  1. <?php                    // -*-c++-*-
  2. // by Edd Dumbill (C) 1999-2002
  3. // $Id: xmlrpc.inc,v 1.124 2006/04/22 21:34:31 ggiunta Exp $
  4.  
  5.  
  6. // Copyright (c) 1999,2000,2002 Edd Dumbill.
  7. // All rights reserved.
  8. //
  9. // Redistribution and use in source and binary forms, with or without
  10. // modification, are permitted provided that the following conditions
  11. // are met:
  12. //
  13. //    * Redistributions of source code must retain the above copyright
  14. //      notice, this list of conditions and the following disclaimer.
  15. //
  16. //    * Redistributions in binary form must reproduce the above
  17. //      copyright notice, this list of conditions and the following
  18. //      disclaimer in the documentation and/or other materials provided
  19. //      with the distribution.
  20. //
  21. //    * Neither the name of the "XML-RPC for PHP" nor the names of its
  22. //      contributors may be used to endorse or promote products derived
  23. //      from this software without specific prior written permission.
  24. //
  25. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  26. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  27. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  28. // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  29. // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  30. // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  31. // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  32. // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  33. // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  34. // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  35. // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  36. // OF THE POSSIBILITY OF SUCH DAMAGE.
  37.  
  38.     
  39.     if(!function_exists('xml_parser_create'))
  40.     {
  41.         // For PHP 4 onward, XML functionality is always compiled-in on windows:
  42.         // no more need to dl-open it. It might have been compiled out on *nix...
  43.                 if(strtoupper(substr(PHP_OS03!= 'WIN'))
  44.         {
  45.             dl('xml.so');
  46.         }
  47.     }
  48.  
  49.     // Try to be backward compat with php < 4.2 (are we not being nice ?)
  50.         if(substr(phpversion()03== '4.0' || substr(phpversion()03== '4.1')
  51.     {
  52.         // give an opportunity to user to specify where to include other files from
  53.                 if(!defined('PHP_XMLRPC_COMPAT_DIR'))
  54.         {
  55.             define('PHP_XMLRPC_COMPAT_DIR',dirname(__FILE__).'/compat/');
  56.         }
  57.         if(substr(phpversion()03== '4.0')
  58.         {
  59.             include(PHP_XMLRPC_COMPAT_DIR."is_scalar.php");
  60.             include(PHP_XMLRPC_COMPAT_DIR."array_key_exists.php");
  61.             include(PHP_XMLRPC_COMPAT_DIR."version_compare.php");
  62.         }
  63.         include(PHP_XMLRPC_COMPAT_DIR."var_export.php");
  64.         include(PHP_XMLRPC_COMPAT_DIR."is_a.php");
  65.     }
  66.  
  67.     // G. Giunta 2005/01/29: declare global these variables,
  68.     // so that xmlrpc.inc will work even if included from within a function
  69.     // Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used.
  70.         $GLOBALS['xmlrpcI4']='i4';
  71.     $GLOBALS['xmlrpcInt']='int';
  72.     $GLOBALS['xmlrpcBoolean']='boolean';
  73.     $GLOBALS['xmlrpcDouble']='double';
  74.     $GLOBALS['xmlrpcString']='string';
  75.     $GLOBALS['xmlrpcDateTime']='dateTime.iso8601';
  76.     $GLOBALS['xmlrpcBase64']='base64';
  77.     $GLOBALS['xmlrpcArray']='array';
  78.     $GLOBALS['xmlrpcStruct']='struct';
  79.     $GLOBALS['xmlrpcValue']='undefined';
  80.  
  81.     $GLOBALS['xmlrpcTypes']=array(
  82.         $GLOBALS['xmlrpcI4']       => 1,
  83.         $GLOBALS['xmlrpcInt']      => 1,
  84.         $GLOBALS['xmlrpcBoolean']  => 1,
  85.         $GLOBALS['xmlrpcString']   => 1,
  86.         $GLOBALS['xmlrpcDouble']   => 1,
  87.         $GLOBALS['xmlrpcDateTime'=> 1,
  88.         $GLOBALS['xmlrpcBase64']   => 1,
  89.         $GLOBALS['xmlrpcArray']    => 2,
  90.         $GLOBALS['xmlrpcStruct']   => 3
  91.     );
  92.  
  93.     $GLOBALS['xmlrpc_valid_parents'array(
  94.         'BOOLEAN' => array('VALUE'),
  95.         'I4' => array('VALUE'),
  96.         'INT' => array('VALUE'),
  97.         'STRING' => array('VALUE'),
  98.         'DOUBLE' => array('VALUE'),
  99.         'DATETIME.ISO8601' => array('VALUE'),
  100.         'BASE64' => array('VALUE'),
  101.         'ARRAY' => array('VALUE'),
  102.         'STRUCT' => array('VALUE'),
  103.         'PARAM' => array('PARAMS'),
  104.         'METHODNAME' => array('METHODCALL'),
  105.         'PARAMS' => array('METHODCALL''METHODRESPONSE'),
  106.         'MEMBER' => array('STRUCT'),
  107.         'NAME' => array('MEMBER'),
  108.         'DATA' => array('ARRAY'),
  109.         'FAULT' => array('METHODRESPONSE'),
  110.         'VALUE' => array('MEMBER''DATA''PARAM''FAULT'),
  111.     );
  112.  
  113.     // define extra types for supporting NULL (useful for json or <NIL/>)
  114.         $GLOBALS['xmlrpcNull']='null';
  115.     $GLOBALS['xmlrpcTypes']['null']=1;
  116.  
  117.     // Not in use anymore since 2.0. Shall we remove it?
  118.     /// @deprecated
  119.         $GLOBALS['xmlEntities']=array(
  120.         'amp'  => '&',
  121.         'quot' => '"',
  122.         'lt'   => '<',
  123.         'gt'   => '>',
  124.         'apos' => "'"
  125.     );
  126.  
  127.     // tables used for transcoding different charsets into us-ascii xml
  128.  
  129.     
  130.     $GLOBALS['xml_iso88591_Entities']=array();
  131.     $GLOBALS['xml_iso88591_Entities']['in'array();
  132.     $GLOBALS['xml_iso88591_Entities']['out'array();
  133.     for ($i 0$i 32$i++)
  134.     {
  135.  
  136.         $GLOBALS['xml_iso88591_Entities']['in'][chr($i);
  137.         $GLOBALS['xml_iso88591_Entities']['out']["&#$i;";
  138.     }
  139.     for ($i 160$i 256$i++)
  140.     {
  141.         $GLOBALS['xml_iso88591_Entities']['in'][chr($i);
  142.         $GLOBALS['xml_iso88591_Entities']['out']["&#$i;";
  143.     }
  144.  
  145.     /// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159.
  146.     /// These will NOT be present in true ISO-8859-1, but will save the unwary
  147.     /// windows user from sending junk.
  148. /*
  149. $cp1252_to_htmlent =
  150.   array(
  151.    '\x80'=>'&#x20AC;', '\x81'=>'?', '\x82'=>'&#x201A;', '\x83'=>'&#x0192;',
  152.    '\x84'=>'&#x201E;', '\x85'=>'&#x2026;', '\x86'=>'&#x2020;', \x87'=>'&#x2021;',
  153.    '\x88'=>'&#x02C6;', '\x89'=>'&#x2030;', '\x8A'=>'&#x0160;', '\x8B'=>'&#x2039;',
  154.    '\x8C'=>'&#x0152;', '\x8D'=>'?', '\x8E'=>'&#x017D;', '\x8F'=>'?',
  155.    '\x90'=>'?', '\x91'=>'&#x2018;', '\x92'=>'&#x2019;', '\x93'=>'&#x201C;',
  156.    '\x94'=>'&#x201D;', '\x95'=>'&#x2022;', '\x96'=>'&#x2013;', '\x97'=>'&#x2014;',
  157.    '\x98'=>'&#x02DC;', '\x99'=>'&#x2122;', '\x9A'=>'&#x0161;', '\x9B'=>'&#x203A;',
  158.    '\x9C'=>'&#x0153;', '\x9D'=>'?', '\x9E'=>'&#x017E;', '\x9F'=>'&#x0178;'
  159.   );
  160. */
  161.  
  162.     $GLOBALS['xmlrpcerr']['unknown_method']=1;
  163.     $GLOBALS['xmlrpcstr']['unknown_method']='Unknown method';
  164.     $GLOBALS['xmlrpcerr']['invalid_return']=2;
  165.     $GLOBALS['xmlrpcstr']['invalid_return']='Invalid return payload: enable debugging to examine incoming payload';
  166.     $GLOBALS['xmlrpcerr']['incorrect_params']=3;
  167.     $GLOBALS['xmlrpcstr']['incorrect_params']='Incorrect parameters passed to method';
  168.     $GLOBALS['xmlrpcerr']['introspect_unknown']=4;
  169.     $GLOBALS['xmlrpcstr']['introspect_unknown']="Can't introspect: method unknown";
  170.     $GLOBALS['xmlrpcerr']['http_error']=5;
  171.     $GLOBALS['xmlrpcstr']['http_error']="Didn't receive 200 OK from remote server.";
  172.     $GLOBALS['xmlrpcerr']['no_data']=6;
  173.     $GLOBALS['xmlrpcstr']['no_data']='No data received from server.';
  174.     $GLOBALS['xmlrpcerr']['no_ssl']=7;
  175.     $GLOBALS['xmlrpcstr']['no_ssl']='No SSL support compiled in.';
  176.     $GLOBALS['xmlrpcerr']['curl_fail']=8;
  177.     $GLOBALS['xmlrpcstr']['curl_fail']='CURL error';
  178.     $GLOBALS['xmlrpcerr']['invalid_request']=15;
  179.     $GLOBALS['xmlrpcstr']['invalid_request']='Invalid request payload';
  180.     $GLOBALS['xmlrpcerr']['no_curl']=16;
  181.     $GLOBALS['xmlrpcstr']['no_curl']='No CURL support compiled in.';
  182.     $GLOBALS['xmlrpcerr']['server_error']=17;
  183.     $GLOBALS['xmlrpcstr']['server_error']='Internal server error';
  184.     $GLOBALS['xmlrpcerr']['multicall_error']=18;
  185.     $GLOBALS['xmlrpcstr']['multicall_error']='Received from server invalid multicall response';
  186.  
  187.     $GLOBALS['xmlrpcerr']['multicall_notstruct'9;
  188.     $GLOBALS['xmlrpcstr']['multicall_notstruct''system.multicall expected struct';
  189.     $GLOBALS['xmlrpcerr']['multicall_nomethod']  10;
  190.     $GLOBALS['xmlrpcstr']['multicall_nomethod']  'missing methodName';
  191.     $GLOBALS['xmlrpcerr']['multicall_notstring'11;
  192.     $GLOBALS['xmlrpcstr']['multicall_notstring''methodName is not a string';
  193.     $GLOBALS['xmlrpcerr']['multicall_recursion'12;
  194.     $GLOBALS['xmlrpcstr']['multicall_recursion''recursive system.multicall forbidden';
  195.     $GLOBALS['xmlrpcerr']['multicall_noparams']  13;
  196.     $GLOBALS['xmlrpcstr']['multicall_noparams']  'missing params';
  197.     $GLOBALS['xmlrpcerr']['multicall_notarray']  14;
  198.     $GLOBALS['xmlrpcstr']['multicall_notarray']  'params is not an array';
  199.  
  200.     $GLOBALS['xmlrpcerr']['cannot_decompress']=103;
  201.     $GLOBALS['xmlrpcstr']['cannot_decompress']='Received from server compressed HTTP and cannot decompress';
  202.     $GLOBALS['xmlrpcerr']['decompress_fail']=104;
  203.     $GLOBALS['xmlrpcstr']['decompress_fail']='Received from server invalid compressed HTTP';
  204.     $GLOBALS['xmlrpcerr']['dechunk_fail']=105;
  205.     $GLOBALS['xmlrpcstr']['dechunk_fail']='Received from server invalid chunked HTTP';
  206.     $GLOBALS['xmlrpcerr']['server_cannot_decompress']=106;
  207.     $GLOBALS['xmlrpcstr']['server_cannot_decompress']='Received from client compressed HTTP request and cannot decompress';
  208.     $GLOBALS['xmlrpcerr']['server_decompress_fail']=107;
  209.     $GLOBALS['xmlrpcstr']['server_decompress_fail']='Received from client invalid compressed HTTP request';
  210.  
  211.     // The charset encoding used by the server for received messages and
  212.     // by the client for received responses when received charset cannot be determined
  213.     // or is not supported
  214.         $GLOBALS['xmlrpc_defencoding']='UTF-8';
  215.     // The encoding used internally by PHP.
  216.     // String values received as xml will be converted to this, and php strings will be converted to xml
  217.  
  218.     // as if having been coded with this
  219.         $GLOBALS['xmlrpc_internalencoding']='ISO-8859-1';
  220.  
  221.     $GLOBALS['xmlrpcName']='XML-RPC for PHP';
  222.     $GLOBALS['xmlrpcVersion']='2.0';
  223.  
  224.     // let user errors start at 800
  225.         $GLOBALS['xmlrpcerruser']=800;
  226.     // let XML parse errors start at 100
  227.         $GLOBALS['xmlrpcerrxml']=100;
  228.  
  229.     // formulate backslashes for escaping regexp
  230.     // Not in use anymore since 2.0. Shall we remove it?
  231.     /// @deprecated
  232.         $GLOBALS['xmlrpc_backslash']=chr(92).chr(92);
  233.  
  234.     // used to store state during parsing
  235.     // quick explanation of components:
  236.     //   ac - used to accumulate values
  237.     //   isf - used to indicate a fault
  238.     //   lv - used to indicate "looking for a value": implements
  239.     //        the logic to allow values with no types to be strings
  240.     //   params - used to store parameters in method calls
  241.     //   method - used to store method name
  242.     //   stack - array with genealogy of xml elements names:
  243.     //           used to validate nesting of xmlrpc elements
  244.  
  245.     
  246.     $GLOBALS['_xh']=null;
  247.  
  248.     /**
  249.     * Convert a string to the correct XML representation in a target charset
  250.     * To help correct communication of non-ascii chars inside strings, regardless
  251.     * of the charset used when sending requests, parsing them, sending responses
  252.     * and parsing responses, an option is to convert all non-ascii chars present in the message
  253.     * into their equivalent 'charset entity'. Charset entities enumerated this way
  254.     * are independent of the charset encoding used to transmit them, and all XML
  255.     * parsers are bound to understand them.
  256.     * Note that in the std case we are not sending a charset encoding mime type
  257.     * along with http headers, so we are bound by RFC 3023 to emit strict us-ascii.
  258.     *
  259.     * @todo do a bit of basic benchmarking (strtr vs. str_replace)
  260.     * @todo    make usage of iconv() or recode_string() or mb_string() where available
  261.     */
  262.     function xmlrpc_encode_entitites($data$src_encoding=''$dest_encoding='')
  263.     {
  264.         if ($src_encoding == '')
  265.         {
  266.             // lame, but we know no better...
  267.             $src_encoding $GLOBALS['xmlrpc_internalencoding'];
  268.         }
  269.         //if ($dest_encoding == '')
  270.         //{
  271.         //    // lame, but we know no better...
  272.         //    $dest_encoding = 'US-ASCII';
  273.         //}
  274.                 switch(strtoupper($src_encoding.'_'.$dest_encoding))
  275.                 {
  276.                     case 'ISO-8859-1_':
  277.                     case 'ISO-8859-1_US-ASCII':
  278.                         $escaped_data str_replace(array('&''"'"'"'<''>')array('&amp;''&quot;''&apos;''&lt;''&gt;')$data);
  279.                         $escaped_data str_replace($GLOBALS['xml_iso88591_Entities']['in']$GLOBALS['xml_iso88591_Entities']['out']$escaped_data);
  280.                         break;
  281.                     case 'ISO-8859-1_UTF-8':
  282.                         $escaped_data str_replace(array('&''"'"'"'<''>')array('&amp;''&quot;''&apos;''&lt;''&gt;')$data);
  283.                         $escaped_data utf8_encode($escaped_data);
  284.                         break;
  285.                     case 'ISO-8859-1_ISO-8859-1':
  286.                     case 'US-ASCII_US-ASCII':
  287.                     case 'US-ASCII_UTF-8':
  288.                     case 'US-ASCII_':
  289.                     case 'US-ASCII_ISO-8859-1':
  290.                     case 'UTF-8_UTF-8':
  291.                         $escaped_data str_replace(array('&''"'"'"'<''>')array('&amp;''&quot;''&apos;''&lt;''&gt;')$data);
  292.                         break;
  293.                     case 'UTF-8_':
  294.                     case 'UTF-8_US-ASCII':
  295.                     case 'UTF-8_ISO-8859-1':
  296.     // NB: this will choke on invalid UTF-8, going most likely beyond EOF
  297.     $escaped_data "";
  298.     // be kind to users creating string xmlrpcvals out of different php types
  299.     $data = (string) $data;
  300.     $ns strlen ($data);
  301.     for ($nn 0$nn $ns$nn++)
  302.     {
  303.         $ch $data[$nn];
  304.         $ii ord($ch);
  305. //1 7 0bbbbbbb (127)
  306.         if ($ii 128)
  307.         {
  308.             /// @todo shall we replace this with a (supposedly) faster str_replace?
  309.             switch($ii){
  310.                 case 34:
  311.                     $escaped_data .= '&quot;';
  312.                     break;
  313.                 case 38:
  314.                     $escaped_data .= '&amp;';
  315.                     break;
  316.                 case 39:
  317.                     $escaped_data .= '&apos;';
  318.                     break;
  319.                 case 60:
  320.                     $escaped_data .= '&lt;';
  321.                     break;
  322.                 case 62:
  323.                     $escaped_data .= '&gt;';
  324.                     break;
  325.                 default:
  326.                     $escaped_data .= $ch;
  327.             // switch
  328.         }
  329. //2 11 110bbbbb 10bbbbbb (2047)
  330.         else if ($ii>>== 6)
  331.         {
  332.             $b1 ($ii 31);
  333.             $ii ord($data[$nn+1]);
  334.             $b2 ($ii 63);
  335.             $ii ($b1 64$b2;
  336.             $ent sprintf ("&#%d;"$ii);
  337.             $escaped_data .= $ent;
  338.         }
  339. //3 16 1110bbbb 10bbbbbb 10bbbbbb
  340.         else if ($ii>>== 14)
  341.         {
  342.             $b1 ($ii 31);
  343.             $ii ord($data[$nn+1]);
  344.             $b2 ($ii 63);
  345.             $ii ord($data[$nn+2]);
  346.             $b3 ($ii 63);
  347.             $ii ((($b1 64$b264$b3;
  348.             $ent sprintf ("&#%d;"$ii);
  349.             $escaped_data .= $ent;
  350.         }
  351. //4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
  352.         else if ($ii>>== 30)
  353.         {
  354.             $b1 ($ii 31);
  355.             $ii ord($data[$nn+1]);
  356.             $b2 ($ii 63);
  357.             $ii ord($data[$nn+2]);
  358.             $b3 ($ii 63);
  359.             $ii ord($data[$nn+3]);
  360.             $b4 ($ii 63);
  361.             $ii ((((($b1 64$b264$b364$b4;
  362.             $ent sprintf ("&#%d;"$ii);
  363.             $escaped_data .= $ent;
  364.         }
  365.     }
  366.                         break;
  367.                     default:
  368.                         $escaped_data '';
  369.                         error_log("Converting from $src_encoding to $dest_encodingnot supported...");
  370.                 }
  371. //        } // switch
  372.         return $escaped_data;
  373.     }
  374.  
  375.     function xmlrpc_se($parser$name$attrs)
  376.     {
  377.         // if invalid xmlrpc already detected, skip all processing
  378.         if ($GLOBALS['_xh']['isf'2)
  379.         {
  380.             // check for correct element nesting
  381.             // top level element can only be of 2 types
  382.             if (count($GLOBALS['_xh']['stack']== 0)
  383.             {
  384.                 if ($name != 'METHODRESPONSE' && $name != 'METHODCALL')
  385.                 {
  386.                     $GLOBALS['_xh']['isf'2;
  387.                     $GLOBALS['_xh']['isf_reason''missing top level xmlrpc element';
  388.                     return;
  389.                 }
  390.             }
  391.             else
  392.             {
  393.                 // not top level element: see if parent is OK
  394.                 $parent end($GLOBALS['_xh']['stack']);
  395.                 if (!array_key_exists($name$GLOBALS['xmlrpc_valid_parents']|| !in_array($parent$GLOBALS['xmlrpc_valid_parents'][$name]))
  396.                 {
  397.                     $GLOBALS['_xh']['isf'2;
  398.                     $GLOBALS['_xh']['isf_reason'"xmlrpc element $name cannot be child of $parent";
  399.                     return;
  400.                 }
  401.             }
  402.  
  403.             switch($name)
  404.             {
  405.                 case 'STRUCT':
  406.                 case 'ARRAY':
  407.                     // create an empty array to hold child values, and push it onto appropriate stack
  408.                     $cur_val array();
  409.                     $cur_val['values'array();
  410.                     $cur_val['type'$name;
  411.                     // check for out-of-band information to rebuild php objs
  412.                     // and in case it is found, save it
  413.                     if (@isset($attrs['PHP_CLASS']))
  414.                     {
  415.                         $cur_val['php_class'$attrs['PHP_CLASS'];
  416.                     }
  417.                     $GLOBALS['_xh']['valuestack'][$cur_val;
  418.                     break;
  419.                 case 'DATA':
  420.                 case 'METHODCALL':
  421.                 case 'METHODRESPONSE':
  422.                 case 'PARAMS':
  423.                     // valid elements that add little to processing
  424.                     break;
  425.                 case 'METHODNAME':
  426.                 case 'NAME':
  427.                     $GLOBALS['_xh']['ac']='';
  428.                     break;
  429.                 case 'FAULT':
  430.                     $GLOBALS['_xh']['isf']=1;
  431.                     break;
  432.                 case 'VALUE':
  433.                     $GLOBALS['_xh']['vt']='value'// indicator: no value found yet
  434.                     $GLOBALS['_xh']['ac']='';
  435.                     $GLOBALS['_xh']['lv']=1;
  436.                     $GLOBALS['_xh']['php_class']=null;
  437.                     break;
  438.                 case 'I4':
  439.                 case 'INT':
  440.                 case 'STRING':
  441.                 case 'BOOLEAN':
  442.                 case 'DOUBLE':
  443.                 case 'DATETIME.ISO8601':
  444.                 case 'BASE64':
  445.                     if ($GLOBALS['_xh']['vt']!='value')
  446.                     {
  447.                         //two data elements inside a value: an error occurred!
  448.                         $GLOBALS['_xh']['isf'2;
  449.                         $GLOBALS['_xh']['isf_reason'"$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
  450.                         return;
  451.                     }
  452.  
  453.                     $GLOBALS['_xh']['ac']=''// reset the accumulator
  454.                     break;
  455.                 case 'MEMBER':
  456.                     $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name']=''// set member name to null, in case we do not find in the xml later on
  457.                     //$GLOBALS['_xh']['ac']='';
  458.                     // Drop trough intentionally
  459.                 case 'PARAM':
  460.                     // clear value type, so we can check later if no value has been passed for this param/member
  461.                     $GLOBALS['_xh']['vt']=null;
  462.                     break;
  463.                 default:
  464.                     /// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
  465.                     $GLOBALS['_xh']['isf'2;
  466.                     $GLOBALS['_xh']['isf_reason'"found not-xmlrpc xml element $name";
  467.                     break;
  468.             }
  469.  
  470.             // Save current element name to stack, to validate nesting
  471.             $GLOBALS['_xh']['stack'][$name;
  472.  
  473.             if($name!='VALUE')
  474.             {
  475.                 $GLOBALS['_xh']['lv']=0;
  476.             }
  477.         }
  478.     }
  479.  
  480.     function xmlrpc_ee($parser$name$rebuild_xmlrpcvals true)
  481.     {
  482.         if ($GLOBALS['_xh']['isf'2)
  483.         {
  484.             // push this element name from stack
  485.             // NB: if XML validates, correct opening/closing is guaranteed and
  486.             // we do not have to check for $name == $curr_elem.
  487.             // we also checked for proper nesting at start of elements...
  488.             $curr_elem array_pop($GLOBALS['_xh']['stack']);
  489.  
  490.             switch($name)
  491.             {
  492.                 case 'STRUCT':
  493.                 case 'ARRAY':
  494.                     // fetch out of stack array of values, and promote it to current value
  495.                     $curr_val array_pop($GLOBALS['_xh']['valuestack']);
  496.                     $GLOBALS['_xh']['value'$curr_val['values'];
  497.                     $GLOBALS['_xh']['vt']=strtolower($name);
  498.                     if (isset($curr_val['php_class']))
  499.                     {
  500.                         $GLOBALS['_xh']['php_class'$curr_val['php_class'];
  501.                     }
  502.                     break;
  503.                 case 'NAME':
  504.                     $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name'$GLOBALS['_xh']['ac'];
  505.                     break;
  506.                 case 'BOOLEAN':
  507.                 case 'I4':
  508.                 case 'INT':
  509.                 case 'STRING':
  510.                 case 'DOUBLE':
  511.                 case 'DATETIME.ISO8601':
  512.                 case 'BASE64':
  513.                     $GLOBALS['_xh']['vt']=strtolower($name);
  514.                     if ($name=='STRING')
  515.                     {
  516.                         $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
  517.                     }
  518.                     elseif ($name=='DATETIME.ISO8601')
  519.                     {
  520.                         /// @todo validate datetime values with a correct format mask?
  521.                         $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcDateTime'];
  522.                         $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
  523.                     }
  524.                     elseif ($name=='BASE64')
  525.                     {
  526.                         /// @todo check for failure of base64 decoding / catch warnings
  527.                         $GLOBALS['_xh']['value']=base64_decode($GLOBALS['_xh']['ac']);
  528.                     }
  529.                     elseif ($name=='BOOLEAN')
  530.                     {
  531.                         // special case here: we translate boolean 1 or 0 into PHP
  532.                         // constants true or false.
  533.                         // Strings 'true' and 'false' are accepted, even though the
  534.                         // spec never mentions them (see eg. Blogger api docs)
  535.                         // NB: this simple checks helps a lot sanitizing input, ie no
  536.                         // security problems around here
  537.                         if ($GLOBALS['_xh']['ac']=='1' || strcasecmp($GLOBALS['_xh']['ac']'true'== 0)
  538.                         {
  539.                             $GLOBALS['_xh']['value']=true;
  540.                         }
  541.                         else
  542.                         {
  543.                             // log if receiveing something strange, even though we set the value to false anyway
  544.                             if ($GLOBALS['_xh']['ac']!='0' && strcasecmp($_xh[$parser]['ac']'false'!= 0)
  545.                                 error_log('XML-RPC: invalid value received in BOOLEAN: '.$GLOBALS['_xh']['ac']);
  546.                             $GLOBALS['_xh']['value']=false;
  547.                         }
  548.                     }
  549.                     elseif ($name=='DOUBLE')
  550.                     {
  551.                         // we have a DOUBLE
  552.                         // we must check that only 0123456789-.<space> are characters here
  553.                         if (!ereg("^[+-]?[eE0123456789 \\t.]+$"$GLOBALS['_xh']['ac']))
  554.                         {
  555.                             /// @todo: find a better way of throwing an error
  556.                             // than this!
  557.                             error_log('XML-RPC: non numeric value received in DOUBLE: '.$GLOBALS['_xh']['ac']);
  558.                             $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
  559.                         }
  560.                         else
  561.                         {
  562.                             // it's ok, add it on
  563.                             $GLOBALS['_xh']['value']=(double)$GLOBALS['_xh']['ac'];
  564.                         }
  565.                     }
  566.                     else
  567.                     {
  568.                         // we have an I4/INT
  569.                         // we must check that only 0123456789-<space> are characters here
  570.                         if (!ereg("^[+-]?[0123456789 \\t]+$"$GLOBALS['_xh']['ac']))
  571.                         {
  572.                             /// @todo find a better way of throwing an error
  573.                             // than this!
  574.                             error_log('XML-RPC: non numeric value received in INT: '.$GLOBALS['_xh']['ac']);
  575.                             $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
  576.                         }
  577.                         else
  578.                         {
  579.                             // it's ok, add it on
  580.                             $GLOBALS['_xh']['value']=(int)$GLOBALS['_xh']['ac'];
  581.                         }
  582.                     }
  583.                     $GLOBALS['_xh']['ac']=''// is this necessary?
  584.                     $GLOBALS['_xh']['lv']=3// indicate we've found a value
  585.                     break;
  586.                 case 'VALUE':
  587.                     // This if() detects if no scalar was inside <VALUE></VALUE>
  588.                     if ($GLOBALS['_xh']['vt']=='value')
  589.                     {
  590.                         $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
  591.                         $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString'];
  592.                     }
  593.  
  594.                     if ($rebuild_xmlrpcvals)
  595.                     {
  596.                         // build the xmlrpc val out of the data received, and substitute it
  597.                         $temp new xmlrpcval($GLOBALS['_xh']['value']$GLOBALS['_xh']['vt']);
  598.                         // in case we got info about underlying php class, save it
  599.                         // in the object we're rebuilding
  600.                         if (isset($GLOBALS['_xh']['php_class']))
  601.                             $temp->_php_class $GLOBALS['_xh']['php_class'];
  602.                         // check if we are inside an array or struct:
  603.                         // if value just built is inside an array, let's move it into array on the stack
  604.                         $vscount count($GLOBALS['_xh']['valuestack']);
  605.                         if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
  606.                         {
  607.                             $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][$temp;
  608.                         }
  609.                         else
  610.                         {
  611.                             $GLOBALS['_xh']['value'$temp;
  612.                         }
  613.                     }
  614.                     else
  615.                     {
  616.                         /// @todo this needs to treat correctly php-serialized objects,
  617.                         /// since std deserializing is done by php_xmlrpc_decode,
  618.                         /// which we will not be calling...
  619.                         if (isset($GLOBALS['_xh']['php_class']))
  620.                         {
  621.                         }
  622.  
  623.                         // check if we are inside an array or struct:
  624.                         // if value just built is inside an array, let's move it into array on the stack
  625.                         $vscount count($GLOBALS['_xh']['valuestack']);
  626.                         if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
  627.                         {
  628.                             $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][$GLOBALS['_xh']['value'];
  629.                         }
  630.                     }
  631.                     break;
  632.                 case 'MEMBER':
  633.                     $GLOBALS['_xh']['ac']=''// is this necessary?
  634.                     // add to array in the stack the last element built,
  635.                     // unless no VALUE was found
  636.                     if ($GLOBALS['_xh']['vt'])
  637.                     {
  638.                         $vscount count($GLOBALS['_xh']['valuestack']);
  639.                         $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][$GLOBALS['_xh']['valuestack'][$vscount-1]['name']] $GLOBALS['_xh']['value'];
  640.                     else
  641.                         error_log('XML-RPC: missing VALUE inside STRUCT in received xml');
  642.                     break;
  643.                 case 'DATA':
  644.                     $GLOBALS['_xh']['ac']=''// is this necessary?
  645.                     break;
  646.                 case 'PARAM':
  647.                     // add to array of params the current value,
  648.                     // unless no VALUE was found
  649.                     if ($GLOBALS['_xh']['vt'])
  650.                     {
  651.                         $GLOBALS['_xh']['params'][]=$GLOBALS['_xh']['value'];
  652.                         $GLOBALS['_xh']['pt'][]=$GLOBALS['_xh']['vt'];
  653.                     }
  654.                     else
  655.                         error_log('XML-RPC: missing VALUE inside PARAM in received xml');
  656.                     break;
  657.                 case 'METHODNAME':
  658.                     $GLOBALS['_xh']['method']=ereg_replace("^[\n\r\t ]+"''$GLOBALS['_xh']['ac']);
  659.                     break;
  660.                 case 'PARAMS':
  661.                 case 'FAULT':
  662.                 case 'METHODCALL':
  663.                 case 'METHORESPONSE':
  664.                     break;
  665.                 default:
  666.                     // End of INVALID ELEMENT!
  667.                     // shall we add an assert here for unreachable code???
  668.                     break;
  669.             }
  670.         }
  671.     }
  672.  
  673.     function xmlrpc_ee_fast($parser$name)
  674.     {
  675.         xmlrpc_ee($parser$namefalse);
  676.     }
  677.  
  678.     function xmlrpc_cd($parser$data)
  679.     {
  680.         //if(ereg("^[\n\r \t]+$", $data)) return;
  681.         // print "adding [${data}]\n";
  682.  
  683.         // skip processing if xml fault already detected
  684.         if ($GLOBALS['_xh']['isf'2)
  685.         {
  686.             if($GLOBALS['_xh']['lv']!=3)
  687.             {
  688.                 // "lookforvalue==3" means that we've found an entire value
  689.                 // and should discard any further character data
  690.                 if($GLOBALS['_xh']['lv']==1)
  691.                 {
  692.                     // if we've found text and we're just in a <value> then
  693.                     // say we've found a value
  694.                     $GLOBALS['_xh']['lv']=2;
  695.                 }
  696.                 if(!@isset($GLOBALS['_xh']['ac']))
  697.                 {
  698.                     $GLOBALS['_xh']['ac''';
  699.                 }
  700.                 $GLOBALS['_xh']['ac'].=$data;
  701.             }
  702.         }
  703.     }
  704.  
  705.     function xmlrpc_dh($parser$data)
  706.     {
  707.         // skip processing if xml fault already detected
  708.         if ($GLOBALS['_xh']['isf'2)
  709.         {
  710.             if(substr($data01== '&' && substr($data-11== ';')
  711.             {
  712.                 if($GLOBALS['_xh']['lv']==1)
  713.                 {
  714.                     $GLOBALS['_xh']['lv']=2;
  715.                 }
  716.                 $GLOBALS['_xh']['ac'].=$data;
  717.             }
  718.         }
  719.     }
  720.  
  721.     class xmlrpc_client
  722.     {
  723.         var $path;
  724.         var $server;
  725.         var $port=0;
  726.         var $method='http';
  727.         var $errno;
  728.         var $errstr;
  729.         var $debug=0;
  730.         var $username='';
  731.         var $password='';
  732.         var $authtype=1;
  733.         var $cert='';
  734.         var $certpass='';
  735.         var $cacert='';
  736.         var $cacertdir='';
  737.         var $key='';
  738.         var $keypass='';
  739.         var $verifypeer=true;
  740.         var $verifyhost=1;
  741.         var $no_multicall=false;
  742.         var $proxy='';
  743.         var $proxyport=0;
  744.         var $proxy_user='';
  745.         var $proxy_pass='';
  746.         var $proxy_authtype=1;
  747.         var $cookies=array();
  748.         /**
  749.         * List of http compression methods accepted by the client for responses.
  750.         * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
  751.         *
  752.         * NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
  753.         * in those cases it will be up to CURL to decide the compression methods
  754.         * it supports. You might check for the presence of 'zlib' in the output of
  755.         * curl_version() to determine wheter compression is supported or not
  756.         */
  757.         var $accepted_compression = array();
  758.         /**
  759.         * Name of compression scheme to be used for sending requests.
  760.         * Either null, gzip or deflate
  761.         */
  762.         var $request_compression = '';
  763.         /**
  764.         * CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
  765.         * http://curl.haxx.se/docs/faq.html#7.3)
  766.         */
  767.         var $xmlrpc_curl_handle = null;
  768.         /// Wheter to use persistent connections for http 1.1 and https
  769.                 var $keepalive = false;
  770.         /// Charset encodings that can be decoded without problems by the client
  771.                 var $accepted_charset_encodings = array();
  772.         /// Charset encoding to be used in serializing request. NULL = use ASCII
  773.                 var $request_charset_encoding = '';
  774.         /**
  775.         * Decides the content of xmlrpcresp objects returned by calls to send()
  776.         * valid strings are 'xmlrpcvals', 'phpvals' or 'xml'
  777.         */
  778.         var $return_type = 'xmlrpcvals';
  779.  
  780.         /**
  781.         * @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php
  782.         * @param string $server the server name / ip address
  783.         * @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used
  784.         * @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed
  785.         */
  786.         function xmlrpc_client($path$server=''$port=''$method='')
  787.         {
  788.             // allow user to specify all params in $path
  789.             if($server == '' and $port == '' and $method == '')
  790.             {
  791.                 $parts parse_url($path);
  792.                 $server $parts['host'];
  793.                 $path $parts['path'];
  794.                 if(isset($parts['query']))
  795.                 {
  796.                     $path .= '?'.$parts['query'];
  797.                 }
  798.                 if(isset($parts['fragment']))
  799.                 {
  800.                     $path .= '#'.$parts['fragment'];
  801.                 }
  802.                 if(isset($parts['port']))
  803.                 {
  804.                     $port $parts['port'];
  805.                 }
  806.                 if(isset($parts['scheme']))
  807.                 {
  808.                     $method $parts['scheme'];
  809.                 }
  810.                 if(isset($parts['user']))
  811.                 {
  812.                     $this->username = $parts['user'];
  813.                 }
  814.                 if(isset($parts['pass']))
  815.                 {
  816.                     $this->password = $parts['pass'];
  817.                 }
  818.             }
  819.             if($path == '' || $path[0!= '/')
  820.             {
  821.                 $this->path='/'.$path;
  822.             }
  823.             else
  824.             {
  825.                 $this->path=$path;
  826.             }
  827.             $this->server=$server;
  828.             if($port != '')
  829.             {
  830.                 $this->port=$port;
  831.             }
  832.             if($method != '')
  833.             {
  834.                 $this->method=$method;
  835.             }
  836.  
  837.             // if ZLIB is enabled, let the client by default accept compressed responses
  838.             if(function_exists('gzinflate'|| (
  839.                 function_exists('curl_init'&& (($info curl_version()) &&
  840.                 ((is_string($info&& strpos($info'zlib'!== null|| isset($info['libz_version'])))
  841.             ))
  842.             {
  843.                 $this->accepted_compression = array('gzip''deflate');
  844.             }
  845.  
  846.             // keepalives: enabled by default ONLY for PHP >= 4.3.8
  847.             // (see http://curl.haxx.se/docs/faq.html#7.3)
  848.             if(version_compare(phpversion()'4.3.8'>= 0)
  849.             {
  850.                 $this->keepalive = true;
  851.             }
  852.  
  853.             // by default the xml parser can support these 3 charset encodings
  854.             $this->accepted_charset_encodings = array('UTF-8''ISO-8859-1''US-ASCII');
  855.         }
  856.  
  857.         /*
  858.         * Enables/disables the echoing to screen of the xmlrpc responses received
  859.         * @param integer $debug values 0, 1 and 2 are supported (2 = echo sent msg too, beside received response)
  860.         * @access public
  861.         */
  862.         function setDebug($in)
  863.         {
  864.             $this->debug=$in;
  865.         }
  866.  
  867.         /*
  868.         * Add some http BASIC AUTH credentials, used by the client to authenticate
  869.         * @param string $u username
  870.         * @param string $p password
  871.         * @param integer $t auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC (basic auth)
  872.         * @access public
  873.         */
  874.         function setCredentials($u$p$t=1)
  875.         {
  876.             $this->username=$u;
  877.             $this->password=$p;
  878.             $this->authtype=$t;
  879.         }
  880.  
  881.         /*
  882.         * Add a client-side https certificate
  883.         * @param string $cert
  884.         * @param string $certpass
  885.         * @access public
  886.         */
  887.         function setCertificate($cert$certpass)
  888.         {
  889.             $this->cert = $cert;
  890.             $this->certpass = $certpass;
  891.         }
  892.  
  893.         /*
  894.         * Add a CA certificate to verify server with (see man page about
  895.         * CURLOPT_CAINFO for more details
  896.         * @param string $cacert certificate file name (or dir holding certificates)
  897.         * @param bool $is_dir set to true to indicate cacert is a dir. defaults to false
  898.         * @access public
  899.         */
  900.         function setCaCertificate($cacert$is_dir=false)
  901.         {
  902.             if ($is_dir)
  903.             {
  904.                 $this->cacert = $cacert;
  905.             }
  906.             else
  907.             {
  908.                 $this->cacertdir = $cacert;
  909.             }
  910.         }
  911.  
  912.         /*
  913.         * @param string $key     The name of a file containing a private SSL key
  914.         * @param string $keypass The secret password needed to use the private SSL key
  915.         * @access public
  916.         * NB: does not work in older php/curl installs
  917.         * Thanks to Daniel Convissor
  918.         */
  919.         function setKey($key$keypass)
  920.         {
  921.             $this->key = $key;
  922.             $this->keypass = $keypass;
  923.         }
  924.  
  925.         /*
  926.         * @param bool $i enable/diable verification of peer certificate
  927.         * @access public
  928.         */
  929.         function setSSLVerifyPeer($i)
  930.         {
  931.             $this->verifypeer = $i;
  932.         }
  933.  
  934.         /*
  935.         * @access public
  936.         */
  937.         function setSSLVerifyHost($i)
  938.         {
  939.             $this->verifyhost = $i;
  940.         }
  941.  
  942.         /**
  943.         * Set proxy info
  944.         *
  945.         * @param    string $proxyhost 
  946.         * @param    string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS
  947.         * @param    string $proxyusername Leave blank if proxy has public access
  948.         * @param    string $proxypassword Leave blank if proxy has public access
  949.         * @param    int    $proxyauthtype set to constant CURLAUTH_MTLM to use NTLM auth with proxy
  950.         * @access   public
  951.         */
  952.         function setProxy($proxyhost$proxyport$proxyusername ''$proxypassword ''$proxyauthtype 1)
  953.         {
  954.             $this->proxy = $proxyhost;
  955.             $this->proxyport = $proxyport;
  956.             $this->proxy_user = $proxyusername;
  957.             $this->proxy_pass = $proxypassword;
  958.             $this->proxy_autthtype $proxyauthtype;
  959.         }
  960.  
  961.         /**
  962.         * Enables/disables reception of compressed xmlrpc responses.
  963.         * Note that enabling reception of compressed responses merely adds some standard
  964.         * http headers to xmlrpc requests. It is up to the xmlrpc server to return
  965.         * compressed responses when receiving such requests.
  966.         * @param string $compmethod either 'gzip', 'deflate', 'any' or ''
  967.         * @access   public
  968.         */
  969.         function setAcceptedCompression($compmethod)
  970.         {
  971.             if ($compmethod == 'any')
  972.                 $this->accepted_compression = array('gzip''deflate');
  973.             else
  974.                 $this->accepted_compression = array($compmethod);
  975.         }
  976.  
  977.         /**
  978.         * Enables/disables http compression of xmlrpc request.
  979.         * Take care when sending compressed requests: servers might not support them
  980.         * (and automatic fallback to uncompressed requests is not yet implemented)
  981.         * @param string $compmethod either 'gzip', 'deflate' or ''
  982.         * @access   public
  983.         */
  984.         function setRequestCompression($compmethod)
  985.         {
  986.             $this->request_compression = $compmethod;
  987.         }
  988.  
  989.         /**
  990.         * Adds a cookie to list of cookies that will be sent to server.
  991.         * NB: setting any param but name and value will turn the cookie into a 'version 1' cookie:
  992.         * do not do it unless you know what you are doing
  993.         * @param string $name 
  994.         * @param string $value 
  995.         * @param string $path 
  996.         * @param string $domain 
  997.         * @param string $port 
  998.         * @access   public
  999.         *
  1000.         * @todo check correctness of urlencoding cookie value (copied from php way of doing it...)
  1001.         */
  1002.         function setCookie($name$value=''$path=''$domain=''$port=null)
  1003.         {
  1004.             $this->cookies[$name]['value'urlencode($value);
  1005.             if ($path || $domain || $port)
  1006.             {
  1007.                 $this->cookies[$name]['path'$path;
  1008.                 $this->cookies[$name]['domain'$domain;
  1009.                 $this->cookies[$name]['port'$port;
  1010.                 $this->cookies[$name]['version'1;
  1011.             }
  1012.             else
  1013.             {
  1014.                 $this->cookies[$name]['version'0;
  1015.             }
  1016.         }
  1017.  
  1018.         /**
  1019.         * Send an xmlrpc request
  1020.         * @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request
  1021.         * @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply
  1022.         * @param string $method if left unspecified, the http protocol chosen during creation of the object will be used
  1023.         */
  1024.         functionsend($msg$timeout=0$method='')
  1025.         {
  1026.             // if user deos not specify http protocol, use native method of this client
  1027.             // (i.e. method set during call to constructor)
  1028.             if($method == '')
  1029.             {
  1030.                 $method $this->method;
  1031.             }
  1032.  
  1033.             if(is_array($msg))
  1034.             {
  1035.                 // $msg is an array of xmlrpcmsg's
  1036.                 $r $this->multicall($msg$timeout$method);
  1037.                 return $r;
  1038.             }
  1039.             elseif(is_string($msg))
  1040.             {
  1041.                 $n new xmlrpcmsg('');
  1042.                 $n->payload $msg;
  1043.                 $msg $n;
  1044.             }
  1045.  
  1046.             // where msg is an xmlrpcmsg
  1047.             $msg->debug=$this->debug;
  1048.  
  1049.             if($method == 'https')
  1050.             {
  1051.                 $r =$this->sendPayloadHTTPS(
  1052.                     $msg,
  1053.                     $this->server,
  1054.                     $this->port,
  1055.                     $timeout,
  1056.                     $this->username,
  1057.                     $this->password,
  1058.                     $this->authtype,
  1059.                     $this->cert,
  1060.                     $this->certpass,
  1061.                     $this->cacert,
  1062.                     $this->cacertdir,
  1063.                     $this->proxy,
  1064.                     $this->proxyport,
  1065.                     $this->proxy_user,
  1066.                     $this->proxy_pass,
  1067.                     $this->proxy_authtype,
  1068.                     $this->keepalive,
  1069.                     $this->key,
  1070.                     $this->keypass
  1071.                 );
  1072.             }
  1073.             elseif($method == 'http11')
  1074.             {
  1075.                 $r =$this->sendPayloadCURL(
  1076.                     $msg,
  1077.                     $this->server,
  1078.                     $this->port,
  1079.                     $timeout,
  1080.                     $this->username,
  1081.                     $this->password,
  1082.                     $this->authtype,
  1083.                     null,
  1084.                     null,
  1085.                     null,
  1086.                     null,
  1087.                     $this->proxy,
  1088.                     $this->proxyport,
  1089.                     $this->proxy_user,
  1090.                     $this->proxy_pass,
  1091.                     $this->proxy_authtype,
  1092.                     'http',
  1093.                     $this->keepalive
  1094.                 );
  1095.             }
  1096.             else
  1097.             {
  1098.                 $r =$this->sendPayloadHTTP10(
  1099.                     $msg,
  1100.                     $this->server,
  1101.                     $this->port,
  1102.                     $timeout,
  1103.                     $this->username,
  1104.                     $this->password,
  1105.                     $this->authtype,
  1106.                     $this->proxy,
  1107.                     $this->proxyport,
  1108.                     $this->proxy_user,
  1109.                     $this->proxy_pass,
  1110.                     $this->proxy_authtype
  1111.                 );
  1112.             }
  1113.  
  1114.             return $r;
  1115.         }
  1116.  
  1117.         /**
  1118.         * @access private
  1119.         */
  1120.         function &sendPayloadHTTP10($msg$server$port$timeout=0,
  1121.             $username=''$password=''$authtype=1$proxyhost='',
  1122.             $proxyport=0$proxyusername=''$proxypassword=''$proxyauthtype=1)
  1123.         {
  1124.             if($port==0)
  1125.             {
  1126.                 $port=80;
  1127.             }
  1128.  
  1129.             // Only create the payload if it was not created previously
  1130.             if(empty($msg->payload))
  1131.             {
  1132.                 $msg->createPayload($this->request_charset_encoding);
  1133.             }
  1134.  
  1135.             $payload $msg->payload;
  1136.             // Deflate request body and set appropriate request headers
  1137.             if(function_exists('gzdeflate'&& ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
  1138.             {
  1139.                 if($this->request_compression == 'gzip')
  1140.                 {
  1141.                     $a @gzencode($msg->payload);
  1142.                     if($a)
  1143.                     {
  1144.                         $payload $a;
  1145.                         $encoding_hdr "Content-Encoding: gzip\r\n";
  1146.                     }
  1147.                 }
  1148.                 else
  1149.                 {
  1150.                     $a @gzdeflate($msg->payload);
  1151.                     if($a)
  1152.                     {
  1153.                         $payload $a;
  1154.                         $encoding_hdr "Content-Encoding: deflate\r\n";
  1155.                     }
  1156.                 }
  1157.             }
  1158.             else
  1159.             {
  1160.                 $encoding_hdr '';
  1161.             }
  1162.  
  1163.             // thanks to Grant Rauscher <[email protected]>
  1164.             // for this
  1165.             $credentials='';
  1166.             if($username!='')
  1167.             {
  1168.                 $credentials='Authorization: Basic ' base64_encode($username ':' $password"\r\n";
  1169.                 if ($authtype != 1)
  1170.                 {
  1171.                     error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth is supported with HTTP 1.0');
  1172.                 }
  1173.             }
  1174.  
  1175.             $accepted_encoding '';
  1176.             if(is_array($this->accepted_compression&& count($this->accepted_compression))
  1177.             {
  1178.                 $accepted_encoding 'Accept-Encoding: ' implode(', '$this->accepted_compression"\r\n";
  1179.             }
  1180.  
  1181.             $proxy_credentials '';
  1182.             if($proxyhost)
  1183.             {
  1184.                 if($proxyport == 0)
  1185.                 {
  1186.                     $proxyport 8080;
  1187.                 }
  1188.                 $connectserver $proxyhost;
  1189.                 $connectport $proxyport;
  1190.                 $uri 'http://'.$server.':'.$port.$this->path;
  1191.                 if($proxyusername != '')
  1192.                 {
  1193.                     if ($proxyauthtype != 1)
  1194.                     {
  1195.                         error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth to proxy is supported with HTTP 1.0');
  1196.                     }
  1197.                     $proxy_credentials 'Proxy-Authorization: Basic ' base64_encode($proxyusername.':'.$proxypassword"\r\n";
  1198.                 }
  1199.             }
  1200.             else
  1201.             {
  1202.                 $connectserver $server;
  1203.                 $connectport $port;
  1204.                 $uri $this->path;
  1205.             }
  1206.  
  1207.             // Cookie generation, as per rfc2965 (version 1 cookies) or
  1208.             // netscape's rules (version 0 cookies)
  1209.             $cookieheader='';
  1210.             foreach ($this->cookies as $name => $cookie)
  1211.             {
  1212.                 if ($cookie['version'])
  1213.                 {
  1214.                     $cookieheader .= 'Cookie: $Version="' $cookie['version''"; ';
  1215.                     $cookieheader .= $name '="' $cookie['value''";';
  1216.                     if ($cookie['path'])
  1217.                         $cookieheader .= ' $Path="' $cookie['path''";';
  1218.                     if ($cookie['domain'])
  1219.                         $cookieheader .= ' $Domain="' $cookie['domain''";';
  1220.                     if ($cookie['port'])
  1221.                         $cookieheader .= ' $Port="' $cookie['domain''";';
  1222.                     $cookieheader substr($cookieheader0-1"\r\n";
  1223.                 }
  1224.                 else
  1225.                 {
  1226.                     $cookieheader .= 'Cookie: ' $name '=' $cookie['value'"\r\n";
  1227.                 }
  1228.             }
  1229.  
  1230.             $op"POST " $uri" HTTP/1.0\r\n" .
  1231.                 "User-Agent: " $GLOBALS['xmlrpcName'" " $GLOBALS['xmlrpcVersion'"\r\n" .
  1232.                 "Host: "$server "\r\n" .
  1233.                 $credentials .
  1234.                 $proxy_credentials .
  1235.                 $accepted_encoding .
  1236.                 $encoding_hdr .
  1237.                 "Accept-Charset: " implode(','$this->accepted_charset_encodings"\r\n" .
  1238.                 $cookieheader .
  1239.                 "Content-Type: " $msg->content_type "\r\nContent-Length: " .
  1240.                 strlen($payload"\r\n\r\n" .
  1241.                 $payload;
  1242.  
  1243.  
  1244.             if($this->debug > 1)
  1245.             {
  1246.                 print "<PRE>\n---SENDING---\n" htmlentities($op"\n---END---\n</PRE>";
  1247.                 // let the client see this now in case http times out...
  1248.                 flush();
  1249.             }
  1250.  
  1251.             if($timeout>0)
  1252.             {
  1253.                 $fp=@fsockopen($connectserver$connectport$this->errno$this->errstr$timeout);
  1254.             }
  1255.             else
  1256.             {
  1257.                 $fp=@fsockopen($connectserver$connectport$this->errno$this->errstr);
  1258.             }
  1259.             if($fp)
  1260.             {
  1261.                 if($timeout>&& function_exists('stream_set_timeout'))
  1262.                 {
  1263.                     stream_set_timeout($fp$timeout);
  1264.                 }
  1265.             }
  1266.             else
  1267.             {
  1268.                 $this->errstr='Connect error: '.$this->errstr;
  1269.                 $r=&new xmlrpcresp(0$GLOBALS['xmlrpcerr']['http_error']$this->errstr . ' (' $this->errno . ')');
  1270.                 return $r;
  1271.             }
  1272.  
  1273.             if(!fputs($fp$opstrlen($op)))
  1274.             {
  1275.                 $this->errstr='Write error';
  1276.                 $r=&new xmlrpcresp(0$GLOBALS['xmlrpcerr']['http_error']$this->errstr);
  1277.                 return $r;
  1278.             }
  1279.             else
  1280.             {
  1281.                 // reset errno and errstr on succesful socket connection
  1282.                 $this->errstr = '';
  1283.             }
  1284.             // G. Giunta 2005/10/24: close socket before parsing.
  1285.             // should yeld slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
  1286.             //$resp=&$msg->parseResponseFile($fp);
  1287.             $ipd='';
  1288.             while($data=fread($fp32768))
  1289.             {
  1290.                 // shall we check for $data === FALSE?
  1291.                 // as per the manual, it signals an error
  1292.                 $ipd.=$data;
  1293.             }
  1294.             fclose($fp);
  1295.             $r =$msg->parseResponse($ipdfalse$this->return_type);
  1296.             return $r;
  1297.  
  1298.         }
  1299.  
  1300.         /**
  1301.         * @access private
  1302.         */
  1303.         function &sendPayloadHTTPS($msg$server$port$timeout=0$username='',
  1304.             $password=''$authtype=1$cert='',$certpass=''$cacert=''$cacertdir='',
  1305.             $proxyhost=''$proxyport=0$proxyusername=''$proxypassword=''$proxyauthtype=1,
  1306.             $keepalive=false$key=''$keypass='')
  1307.         {
  1308.             $r =$this->sendPayloadCURL($msg$server$port$timeout$username,
  1309.                 $password$authtype$cert$certpass$cacert$cacertdir$proxyhost$proxyport,
  1310.                 $proxyusername$proxypassword$proxyauthtype'https'$keepalive$key$keypass);
  1311.             return $r;
  1312.         }
  1313.  
  1314.         /**
  1315.         * Contributed by Justin Miller <[email protected]>
  1316.         * Requires curl to be built into PHP
  1317.         * NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
  1318.         * @access private
  1319.         */
  1320.         function &sendPayloadCURL($msg$server$port$timeout=0$username='',
  1321.             $password=''$authtype=1$cert=''$certpass=''$cacert=''$cacertdir='',
  1322.             $proxyhost=''$proxyport=0$proxyusername=''$proxypassword=''$proxyauthtype=1$method='https',
  1323.             $keepalive=false$key=''$keypass='')
  1324.         {
  1325.             if(!function_exists('curl_init'))
  1326.             {
  1327.                 $this->errstr='CURL unavailable on this install';
  1328.                 $r=&new xmlrpcresp(0$GLOBALS['xmlrpcerr']['no_curl']$GLOBALS['xmlrpcstr']['no_curl']);
  1329.                 return $r;
  1330.             }
  1331.             if($method == 'https')
  1332.             {
  1333.                 if(($info curl_version()) &&
  1334.                     ((is_string($info&& strpos($info'OpenSSL'=== null|| (is_array($info&& !isset($info['ssl_version']))))
  1335.                 {
  1336.                     $this->errstr='SSL unavailable on this install';
  1337.                     $r=&new xmlrpcresp(0$GLOBALS['xmlrpcerr']['no_ssl']$GLOBALS['xmlrpcstr']['no_ssl']);
  1338.                     return $r;
  1339.                 }
  1340.             }
  1341.  
  1342.             if($port == 0)
  1343.             {
  1344.                 if($method == 'http')
  1345.                 {
  1346.                     $port 80;
  1347.                 }
  1348.                 else
  1349.                 {
  1350.                     $port 443;
  1351.                 }
  1352.             }
  1353.  
  1354.             // Only create the payload if it was not created previously
  1355.             if(empty($msg->payload))
  1356.             {
  1357.                 $msg->createPayload($this->request_charset_encoding);
  1358.             }
  1359.  
  1360.             // Deflate request body and set appropriate request headers
  1361.             $payload $msg->payload;
  1362.             if(function_exists('gzdeflate'&& ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
  1363.             {
  1364.                 if($this->request_compression == 'gzip')
  1365.                 {
  1366.                     $a @gzencode($msg->payload);
  1367.                     if($a)
  1368.                     {
  1369.                         $payload $a;
  1370.                         $encoding_hdr "Content-Encoding: gzip";
  1371.                     }
  1372.                 }
  1373.                 else
  1374.                 {
  1375.                     $a @gzdeflate($msg->payload);
  1376.                     if($a)
  1377.                     {
  1378.                         $payload $a;
  1379.                         $encoding_hdr "Content-Encoding: deflate";
  1380.                     }
  1381.                 }
  1382.             }
  1383.             else
  1384.             {
  1385.                 $encoding_hdr '';
  1386.             }
  1387.  
  1388.             if($this->debug > 1)
  1389.             {
  1390.                 print "<PRE>\n---SENDING---\n" htmlentities($payload"\n---END---\n</PRE>";
  1391.                 // let the client see this now in case http times out...
  1392.                 flush();
  1393.             }
  1394.  
  1395.             if(!$keepalive || !$this->xmlrpc_curl_handle)
  1396.             {
  1397.                 $curl curl_init($method '://' $server ':' $port $this->path);
  1398.                 if($keepalive)
  1399.                 {
  1400.                     $this->xmlrpc_curl_handle = $curl;
  1401.                 }
  1402.             }
  1403.             else
  1404.             {
  1405.                 $curl $this->xmlrpc_curl_handle;
  1406.             }
  1407.  
  1408.             // results into variable
  1409.             curl_setopt($curlCURLOPT_RETURNTRANSFER1);
  1410.  
  1411.             if($this->debug)
  1412.             {
  1413.                 curl_setopt($curlCURLOPT_VERBOSE1);
  1414.             }
  1415.             curl_setopt($curlCURLOPT_USERAGENT$GLOBALS['xmlrpcName'].' '.$GLOBALS['xmlrpcVersion']);
  1416.             // required for XMLRPC: post the data
  1417.             curl_setopt($curlCURLOPT_POST1);
  1418.             // the data
  1419.             curl_setopt($curlCURLOPT_POSTFIELDS$payload);
  1420.  
  1421.             // return the header too
  1422.             curl_setopt($curlCURLOPT_HEADER1);
  1423.  
  1424.             // will only work with PHP >= 5.0
  1425.             // NB: if we set an empty string, CURL will add http header indicating
  1426.             // ALL methods it is supporting. This is possibly a better option than
  1427.             // letting the user tell what curl can / cannot do...
  1428.             if(is_array($this->accepted_compression&& count($this->accepted_compression))
  1429.             {
  1430.                 //curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
  1431.                 // empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
  1432.                 curl_setopt($curlCURLOPT_ENCODING'');
  1433.             }
  1434.             // extra headers
  1435.             $headers array('Content-Type: ' $msg->content_type 'Accept-Charset: ' implode(','$this->accepted_charset_encodings));
  1436.             // if no keepalive is wanted, let the server know it in advance
  1437.             if(!$keepalive)
  1438.             {
  1439.                 $headers['Connection: close';
  1440.             }
  1441.             // request compression header
  1442.             if($encoding_hdr)
  1443.             {
  1444.                 $headers[$encoding_hdr;
  1445.             }
  1446.  
  1447.             curl_setopt($curlCURLOPT_HTTPHEADER$headers);
  1448.             // timeout is borked
  1449.             if($timeout)
  1450.             {
  1451.                 curl_setopt($curlCURLOPT_TIMEOUT$timeout == $timeout 1);
  1452.             }
  1453.  
  1454.             if($username && $password)
  1455.             {
  1456.                 curl_setopt($curlCURLOPT_USERPWD,"$username:$password");
  1457.                 if (defined('CURLOPT_HTTPAUTH'))
  1458.                 {
  1459.                     curl_setopt($curlCURLOPT_HTTPAUTH$authtype);
  1460.                 }
  1461.                 else if ($authtype != 1)
  1462.                 {
  1463.                     error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth is supported by the current PHP/curl install');
  1464.                 }
  1465.             }
  1466.  
  1467.             if($method == 'https')
  1468.             {
  1469.                 // set cert file
  1470.                 if($cert)
  1471.                 {
  1472.                     curl_setopt($curlCURLOPT_SSLCERT$cert);
  1473.                 }
  1474.                 // set cert password
  1475.                 if($certpass)
  1476.                 {
  1477.                     curl_setopt($curlCURLOPT_SSLCERTPASSWD$certpass);
  1478.                 }
  1479.                 // whether to verify remote host's cert
  1480.                 curl_setopt($curlCURLOPT_SSL_VERIFYPEER$this->verifypeer);
  1481.                 // set ca certificates file/dir
  1482.                 if($cacert)
  1483.                 {
  1484.                     curl_setopt($curlCURLOPT_CAINFO$cacert);
  1485.                 }
  1486.                 if($cacertdir)
  1487.                 {
  1488.                     curl_setopt($curlCURLOPT_CAPATH$cacertdir);
  1489.                 }
  1490.                 // set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
  1491.                 if($key)
  1492.                 {
  1493.                     curl_setopt($curlCURLOPT_SSLKEY$key);
  1494.                 }
  1495.                 // set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
  1496.                 if($keypass)
  1497.                 {
  1498.                     curl_setopt($curlCURLOPT_SSLKEYPASSWD$keypass);
  1499.                 }
  1500.                 // whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that it matches the hostname used
  1501.                 curl_setopt($curlCURLOPT_SSL_VERIFYHOST$this->verifyhost);
  1502.             }
  1503.  
  1504.             // proxy info
  1505.             if($proxyhost)
  1506.             {
  1507.                 if($proxyport == 0)
  1508.                 {
  1509.                     $proxyport 8080// NB: even for HTTPS, local connection is on port 8080
  1510.                 }
  1511.                 curl_setopt($curlCURLOPT_PROXY,$proxyhost.':'.$proxyport);
  1512.                 //curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport);
  1513.                 if($proxyusername)
  1514.                 {
  1515.                     curl_setopt($curlCURLOPT_PROXYUSERPWD$proxyusername.':'.$proxypassword);
  1516.                     if (defined('CURLOPT_PROXYAUTH'))
  1517.                     {
  1518.                         curl_setopt($curlCURLOPT_PROXYAUTH$proxyauthtype);
  1519.                     }
  1520.                     else if ($proxyauthtype != 1)
  1521.                     {
  1522.                         error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth to proxy is supported by the current PHP/curl install');
  1523.                     }
  1524.                 }
  1525.             }
  1526.  
  1527.             // NB: should we build cookie http headers by hand rather than let CURL do it?
  1528.             // the following code does not honour 'expires', 'path' and 'domain' cookie attributes
  1529.             // set to clint obj the the user...
  1530.             if (count($this->cookies))
  1531.             {
  1532.                 $cookieheader '';
  1533.                 foreach ($this->cookies as $name => $cookie)
  1534.                 {
  1535.                     $cookieheader .= $name '=' $cookie['value'', ';
  1536.                 }
  1537.                 curl_setopt($curlCURLOPT_COOKIEsubstr($cookieheader0-2));
  1538.             }
  1539.  
  1540.             $result curl_exec($curl);
  1541.  
  1542.             if(!$result)
  1543.             {
  1544.                 $this->errstr='no response';
  1545.                 $resp=&new xmlrpcresp(0$GLOBALS['xmlrpcerr']['curl_fail']$GLOBALS['xmlrpcstr']['curl_fail']': 'curl_error($curl));
  1546.                 if(!$keepalive)
  1547.                 {
  1548.                     curl_close($curl);
  1549.                 }
  1550.             }
  1551.             else
  1552.             {
  1553.                 if(!$keepalive)
  1554.                 {
  1555.                     curl_close($curl);
  1556.                 }
  1557.                 $resp =$msg->parseResponse($resulttrue$this->return_type);
  1558.             }
  1559.             return $resp;
  1560.         }
  1561.  
  1562.         /**
  1563.         * Send an array of request messages and return an array of responses.
  1564.         * Unless $this->no_multicall has been set to true, it will try first
  1565.         * to use one single xmlrpc call to server method system.multicall, and
  1566.         * revert to sending many successive calls in case of failure.
  1567.         * This failure is also stored in $this->no_multicall for subsequent calls.
  1568.         * Unfortunately, there is no server error code universally used to denote
  1569.         * the fact that multicall is unsupported, so there is no way to reliably
  1570.         * distinguish between that and a temporary failure.
  1571.         * If you are sure that server supports multicall and do not want to
  1572.         * fallback to using many single calls, set the fourth parameter to FALSE.
  1573.         *
  1574.         * NB: trying to shoehorn extra functionality into existing syntax has resulted
  1575.         * in pretty much convoluted code...
  1576.         *
  1577.         * @access public
  1578.         * @param array $msgs an array of xmlrpcmsg objects
  1579.         * @param integer $timeout connection timeout (in seconds)
  1580.         * @param string $method the http protocol variant to be used
  1581.         * @param boolen fallback When true, upon receiveing an error during multicall, multiple single calls will be attempted
  1582.         */
  1583.         function multicall($msgs$timeout=0$method='http'$fallback=true)
  1584.         {
  1585.             if(!$this->no_multicall)
  1586.             {
  1587.                 $results $this->_try_multicall($msgs$timeout$method);
  1588.                 if(is_array($results))
  1589.                 {
  1590.                     // System.multicall succeeded
  1591.                     return $results;
  1592.                 }
  1593.                 else
  1594.                 {
  1595.                     // either system.multicall is unsupported by server,
  1596.                     // or call failed for some other reason.
  1597.                     if ($fallback)
  1598.                     {
  1599.                         // Don't try it next time...
  1600.                         $this->no_multicall = true;
  1601.                     }
  1602.                     else
  1603.                     {
  1604.                         if (is_a($results'xmlrpcresp'))
  1605.                         {
  1606.                             $result $results;
  1607.                         }
  1608.                         else
  1609.                         {
  1610.                             $result new xmlrpcresp(0$GLOBALS['xmlrpcerr']['multicall_error']$GLOBALS['xmlrpcstr']['multicall_error']);
  1611.                         }
  1612.                     }
  1613.                 }
  1614.             }
  1615.             else
  1616.             {
  1617.                 // override fallback, in case careless user tries to do two
  1618.                 // opposite things at the same time
  1619.                 $fallback true;
  1620.             }
  1621.  
  1622.             $results array();
  1623.             if ($fallback)
  1624.             {
  1625.                 // system.multicall is (probably) unsupported by server:
  1626.                 // emulate multicall via multiple requests
  1627.                 foreach($msgs as $msg)
  1628.                 {
  1629.                     $results[=$this->send($msg$timeout$method);
  1630.                 }
  1631.             }
  1632.             else
  1633.             {
  1634.                 // user does NOT want to fallback on many single calls:
  1635.                 // since we should always return an array of responses,
  1636.                 // return an array with the same error repeated n times
  1637.                 foreach($msgs as $msg)
  1638.                 {
  1639.                     $results[$result;
  1640.                 }
  1641.             }
  1642.             return $results;
  1643.         }
  1644.  
  1645.         /**
  1646.         * Attempt to boxcar $msgs via system.multicall.
  1647.         * Returns either an array of xmlrpcreponses, an xmlrpc error response
  1648.         * or false (when recived response does not respect valid multiccall syntax)
  1649.         * @access private
  1650.         */
  1651.         function _try_multicall($msgs$timeout$method)
  1652.         {
  1653.             // Construct multicall message
  1654.             $calls array();
  1655.             foreach($msgs as $msg)
  1656.             {
  1657.                 $call['methodName'new xmlrpcval($msg->method(),'string');
  1658.                 $numParams $msg->getNumParams();
  1659.                 $params array();
  1660.                 for($i 0$i $numParams$i++)
  1661.                 {
  1662.                     $params[$i$msg->getParam($i);
  1663.                 }
  1664.                 $call['params'new xmlrpcval($params'array');
  1665.                 $calls[new xmlrpcval($call'struct');
  1666.             }
  1667.             $multicall new xmlrpcmsg('system.multicall');
  1668.             $multicall->addParam(new xmlrpcval($calls'array'));
  1669.  
  1670.             // Attempt RPC call
  1671.             $result =$this->send($multicall$timeout$method);
  1672.             //if(!is_object($result))
  1673.             //{
  1674.             //    return ($result || 0); // transport failed
  1675.             //}
  1676.  
  1677.             if($result->faultCode(!= 0)
  1678.             {
  1679.                 // call to system.multicall failed
  1680.                 return $result;
  1681.             }
  1682.  
  1683.             // Unpack responses.
  1684.             $rets $result->value();
  1685.  
  1686.             if ($this->return_type == 'xml')
  1687.             {
  1688.                     return $rets;
  1689.             }
  1690.             else if ($this->return_type == 'phpvals')
  1691.             {
  1692.                 ///@todo test this code branch...
  1693.                 $rets $result->value();
  1694.                 if(!is_array($rets))
  1695.                 {
  1696.                     return false;        // bad return type from system.multicall
  1697.                 }
  1698.                 $numRets count($rets);
  1699.                 if($numRets != count($msgs))
  1700.                 {
  1701.                     return false;        // wrong number of return values.
  1702.                 }
  1703.  
  1704.                 $response array();
  1705.                 for($i 0$i $numRets$i++)
  1706.                 {
  1707.                     $val $rets[$i];
  1708.                     if (!is_array($val)) {
  1709.                         return false;
  1710.                     }
  1711.                     switch(count($val))
  1712.                     {
  1713.                         case 1:
  1714.                             if(!isset($val[0]))
  1715.                             {
  1716.                                 return false;        // Bad value
  1717.                             }
  1718.                             // Normal return value
  1719.                             $response[$inew xmlrpcresp($val[0]0'''phpvals');
  1720.                             break;
  1721.                         case 2:
  1722.                             ///    @todo remove usage of @: it is apparently quite slow
  1723.                             $code @$val['faultCode'];
  1724.                             if(!is_int($code))
  1725.                             {
  1726.                                 return false;
  1727.                             }
  1728.                             $str @$val['faultString'];
  1729.                             if(!is_string($str))
  1730.                             {
  1731.                                 return false;
  1732.                             }
  1733.                             $response[$inew xmlrpcresp(0$code$str);
  1734.                             break;
  1735.                         default:
  1736.                             return false;
  1737.                     }
  1738.                 }
  1739.                 return $response;
  1740.             }
  1741.             else // return type == 'xmlrpcvals'
  1742.             {
  1743.                 $rets $result->value();
  1744.                 if($rets->kindOf(!= 'array')
  1745.                 {
  1746.                     return false;        // bad return type from system.multicall
  1747.                 }
  1748.                 $numRets $rets->arraysize();
  1749.                 if($numRets != count($msgs))
  1750.                 {
  1751.                     return false;        // wrong number of return values.
  1752.                 }
  1753.  
  1754.                 $response array();
  1755.                 for($i 0$i $numRets$i++)
  1756.                 {
  1757.                     $val $rets->arraymem($i);
  1758.                     switch($val->kindOf())
  1759.                     {
  1760.                         case 'array':
  1761.                             if($val->arraysize(!= 1)
  1762.                             {
  1763.                                 return false;        // Bad value
  1764.                             }
  1765.                             // Normal return value
  1766.                             $response[$inew xmlrpcresp($val->arraymem(0));
  1767.                             break;
  1768.                         case 'struct':
  1769.                             $code $val->structmem('faultCode');
  1770.                             if($code->kindOf(!= 'scalar' || $code->scalartyp(!= 'int')
  1771.                             {
  1772.                                 return false;
  1773.                             }
  1774.                             $str $val->structmem('faultString');
  1775.                             if($str->kindOf(!= 'scalar' || $str->scalartyp(!= 'string')
  1776.                             {
  1777.                                 return false;
  1778.                             }
  1779.                             $response[$inew xmlrpcresp(0$code->scalarval()$str->scalarval());
  1780.                             break;
  1781.                         default:
  1782.                             return false;
  1783.                     }
  1784.                 }
  1785.                 return $response;
  1786.             }
  1787.         }
  1788.     // end class xmlrpc_client
  1789.  
  1790.     
  1791.     class xmlrpcresp
  1792.     {
  1793.         var $val = 0;
  1794.         var $valtyp;
  1795.         var $errno = 0;
  1796.         var $errstr = '';
  1797.         var $payload;
  1798.         var $hdrs = array();
  1799.         var $_cookies = array();
  1800.         var $content_type = 'text/xml';
  1801.  
  1802.         /**
  1803.         * @param mixed  $val either an xmlrpcval obj, a php value or the xml serialization of an xmlrpcval (a string)
  1804.         * @param integer $fcode set it to anything but 0 to create an error response
  1805.         * @param string $fstr the error string, in case of an error response
  1806.         * @param string $valtyp either 'xmlrpcvals', 'phpvals' or 'xml'
  1807.         *
  1808.         * @todo add check that $val is of correct type???
  1809.         *  NB: as of now we do not do it, since it might be either an xmlrpcval or a plain
  1810.         *  php val, or a complete xml chunk, depending on usage of xmlrpc_client::send() inside which creator is called...
  1811.         */
  1812.         function xmlrpcresp($val$fcode 0$fstr ''$valtyp='')
  1813.         {
  1814.             if($fcode != 0)
  1815.             {
  1816.                 // error response
  1817.                 $this->errno = $fcode;
  1818.                 $this->errstr = $fstr;
  1819.                 //$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
  1820.             }
  1821.             /*elseif(!is_object($val) || !is_a($val, 'xmlrpcval'))
  1822.             {
  1823.                 // programmer error
  1824.                 error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to xmlrpcresp. Defaulting to empty value.");
  1825.                 $this->val =& new xmlrpcval();
  1826.             }*/
  1827.             else
  1828.             {
  1829.                 // successful response
  1830.                 $this->val = $val;
  1831.                 if ($valtyp == '')
  1832.                 {
  1833.                     // user did not declare type of response value: try to guess it
  1834.                     if (is_object($this->val&& is_a($this->val'xmlrpcval'))
  1835.                     {
  1836.                         $this->valtyp = 'xmlrpcvals';
  1837.                     }
  1838.                     else if (is_string($this->val))
  1839.                     {
  1840.                         $this->valtyp = 'xml';
  1841.  
  1842.                     }
  1843.                     else
  1844.                     {
  1845.                         $this->valtyp = 'phpvals';
  1846.                     }
  1847.                 }
  1848.                 else
  1849.                 {
  1850.                     // user declares type of resp value: believe him
  1851.                     $this->valtyp = $valtyp;
  1852.                 }
  1853.             }
  1854.         }
  1855.  
  1856.         /*
  1857.         * @return integer the error code of this response (0 for not-error responses)
  1858.         */
  1859.         function faultCode()
  1860.         {
  1861.             return $this->errno;
  1862.         }
  1863.  
  1864.         /*
  1865.         * @return string the error string of this response ('' for not-error responses)
  1866.         */
  1867.         function faultString()
  1868.         {
  1869.             return $this->errstr;
  1870.         }
  1871.  
  1872.         /*
  1873.         * @return mixed the xmlrpcval object returned by the server. Might be an xml string or php value if the response has been created by specially configured xmlrpc_client objects
  1874.         */
  1875.         function value()
  1876.         {
  1877.             return $this->val;
  1878.         }
  1879.  
  1880.         /**
  1881.         * Returns an array with the cookies received from the server.
  1882.         * Array has the form: $cookiename => array ('value' => $val, $attr1 => $val1, $attr2 = $val2, ...)
  1883.         * with attributes being e.g. 'expires', 'path', domain'.
  1884.         * NB: cookies sent as 'expired' by the server (i.e. with an expiry date in the past)
  1885.         * are still present in the array. It is up to the user-defined code to decide
  1886.         * how to use the received cookies, and wheter they have to be sent back with the next
  1887.         * request to the server (using xmlrpc_client::setCookie) or not
  1888.         * @return array array of cookies received from the server
  1889.         * @access public
  1890.         */
  1891.         function cookies()
  1892.         {
  1893.             return $this->_cookies;
  1894.         }
  1895.  
  1896.         /**
  1897.         * Return xml representation of the response
  1898.         * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
  1899.         * @return string the xml representation of the response
  1900.         */
  1901.         function serialize($charset_encoding='')
  1902.         {
  1903.             if ($charset_encoding != '')
  1904.                 $this->content_type = 'text/xml; charset=' $charset_encoding;
  1905.             else
  1906.                 $this->content_type = 'text/xml';
  1907.             $result "<methodResponse>\n";
  1908.             if($this->errno)
  1909.             {
  1910.                 // G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
  1911.                 // by xml-encoding non ascii chars
  1912.                 $result .= "<fault>\n" .
  1913. "<value>\n<struct><member><name>faultCode</name>\n<value><int>" $this->errno .
  1914. "</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>" .
  1915. xmlrpc_encode_entitites($this->errstr$GLOBALS['xmlrpc_internalencoding']$charset_encoding"</string></value>\n</member>\n" .
  1916. "</struct>\n</value>\n</fault>";
  1917.             }
  1918.             else
  1919.             {
  1920.                 if(!is_object($this->val|| !is_a($this->val'xmlrpcval'))
  1921.                 {
  1922.                     if (is_string($this->val&& $this->valtyp == 'xml')
  1923.                     {
  1924.                         $result .= "<params>\n<param>\n" .
  1925.                             $this->val .
  1926.                             "</param>\n</params>";
  1927.                     }
  1928.                     else
  1929.                     {
  1930.                         /// @todo try to build something serializable?
  1931.                         die('cannot serialize xmlrpcresp objects whose content is native php values');
  1932.                     }
  1933.                 }
  1934.                 else
  1935.                 {
  1936.                     $result .= "<params>\n<param>\n" .
  1937.                         $this->val->serialize($charset_encoding.
  1938.                         "</param>\n</params>";
  1939.                 }
  1940.             }
  1941.             $result .= "\n</methodResponse>";
  1942.             $this->payload = $result;
  1943.             return $result;
  1944.         }
  1945.     }
  1946.  
  1947.     class xmlrpcmsg
  1948.     {
  1949.         var $payload;
  1950.         var $methodname;
  1951.         var $params=array();
  1952.         var $debug=0;
  1953.         var $content_type = 'text/xml';
  1954.  
  1955.         /*
  1956.         * @param string $meth the name of the method to invoke
  1957.         * @param array $pars array of parameters to be paased to the method (xmlrpcval objects)
  1958.         */
  1959.         function xmlrpcmsg($meth$pars=0)
  1960.         {
  1961.             $this->methodname=$meth;
  1962.             if(is_array($pars&& sizeof($pars)>0)
  1963.             {
  1964.                 for($i=0$i<sizeof($pars)$i++)
  1965.                 {
  1966.                     $this->addParam($pars[$i]);
  1967.                 }
  1968.             }
  1969.         }
  1970.  
  1971.         function xml_header($charset_encoding='')
  1972.         {
  1973.             if ($charset_encoding != '')
  1974.             {
  1975.                 return "<?xml version=\"1.0\" encoding=\"$charset_encoding\" ?">\n<methodCall>\n";
  1976.             }
  1977.             else
  1978.             {
  1979.                 return "<?xml version=\"1.0\"?" ">\n<methodCall>\n";
  1980.             }
  1981.         }
  1982.  
  1983.         function xml_footer()
  1984.         {
  1985.             return "</methodCall>";
  1986.         }
  1987.  
  1988.         function kindOf()
  1989.         {
  1990.             return 'msg';
  1991.         }
  1992.  
  1993.         function createPayload($charset_encoding='')
  1994.         {
  1995.             if ($charset_encoding != '')
  1996.                 $this->content_type = 'text/xml; charset=' $charset_encoding;
  1997.             else
  1998.                 $this->content_type = 'text/xml';
  1999.             $this->payload=$this->xml_header($charset_encoding);
  2000.             $this->payload.='<methodName>' $this->methodname . "</methodName>\n";
  2001.             //    if(sizeof($this->params)) {
  2002.             $this->payload.="<params>\n";
  2003.             for($i=0$i<sizeof($this->params)$i++)
  2004.             {
  2005.                 $p=$this->params[$i];
  2006.                 $this->payload.="<param>\n" $p->serialize($charset_encoding.
  2007.                 "</param>\n";
  2008.             }
  2009.             $this->payload.="</params>\n";
  2010.             // }
  2011.             $this->payload.=$this->xml_footer();
  2012.             //$this->payload=str_replace("\n", "\r\n", $this->payload);
  2013.         }
  2014.  
  2015.         /*
  2016.         * Gets/sets the xmlrpc method to be invoked
  2017.         * @param string $meth the method to be set (leave empty not to set it)
  2018.         * @return string the method that will be invoked
  2019.         * @access public
  2020.         */
  2021.         function method($meth='')
  2022.         {
  2023.             if($meth!='')
  2024.             {
  2025.                 $this->methodname=$meth;
  2026.             }
  2027.             return $this->methodname;
  2028.         }
  2029.  
  2030.         /*
  2031.         * @return string the xml representation of the message
  2032.         */
  2033.         function serialize($charset_encoding='')
  2034.         {
  2035.             $this->createPayload($charset_encoding);
  2036.             return $this->payload;
  2037.         }
  2038.  
  2039.         /*
  2040.         * Add a parameter to the list of parameters to be used upon method invocation
  2041.         * @param xmlrpcval $par
  2042.         * @return boolean false on failure
  2043.         */
  2044.         function addParam($par)
  2045.         {
  2046.             // add check: do not add to self params which are not xmlrpcvals
  2047.             if(is_object($par&& is_a($par'xmlrpcval'))
  2048.             {
  2049.                 $this->params[]=$par;
  2050.                 return true;
  2051.             }
  2052.             else
  2053.             {
  2054.                 return false;
  2055.             }
  2056.         }
  2057.  
  2058.         /*
  2059.         * @param integer $i the index of the parameter to fetch (zero based)
  2060.         * @return xmlrpcval the i-th parameter
  2061.         */
  2062.         function getParam($ireturn $this->params[$i]}
  2063.  
  2064.         /*
  2065.         * @return integer the number of parameters currently set
  2066.         */
  2067.         function getNumParams(return sizeof($this->params)}
  2068.  
  2069.         /*
  2070.         * @access private
  2071.         * @todo add 2nd & 3rd param to be passed to ParseResponse() ???
  2072.         */
  2073.         function &parseResponseFile($fp)
  2074.         {
  2075.             $ipd='';
  2076.             while($data=fread($fp32768))
  2077.             {
  2078.                 $ipd.=$data;
  2079.             }
  2080.             //fclose($fp);
  2081.             $r =$this->parseResponse($ipd);
  2082.             return $r;
  2083.         }
  2084.  
  2085.         /**
  2086.         * Parses HTTP headers and separates them from data.
  2087.         * @access private
  2088.         */
  2089.         function &parseResponseHeaders(&$data$headers_processed=false)
  2090.         {
  2091.                 // Strip HTTP 1.1 100 Continue header if present
  2092.                 while(ereg('^HTTP/1\.1 1[0-9]{2} '$data))
  2093.                 {
  2094.                     $pos strpos($data'HTTP'12);
  2095.                     // server sent a Continue header without any (valid) content following...
  2096.                     // give the client a chance to know it
  2097.                     if(!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
  2098.                     {
  2099.                         break;
  2100.                     }
  2101.                     $data substr($data$pos);
  2102.                 }
  2103.                 if(!ereg('^HTTP/[0-9.]+ 200 '$data))
  2104.                 {
  2105.                     $errstrsubstr($data0strpos($data"\n")-1);
  2106.                     error_log('XML-RPC: xmlrpcmsg::parseResponse: HTTP error, got response: ' .$errstr);
  2107.                     $r=&new xmlrpcresp(0$GLOBALS['xmlrpcerr']['http_error']$GLOBALS['xmlrpcstr']['http_error']' (' $errstr ')');
  2108.                     return $r;
  2109.                 }
  2110.  
  2111.                 $GLOBALS['_xh']['headers'array();
  2112.                 $GLOBALS['_xh']['cookies'array();
  2113.  
  2114.                 // be tolerant to usage of \n instead of \r\n to separate headers and data
  2115.                 // (even though it is not valid http)
  2116.                 $pos strpos($data,"\r\n\r\n");
  2117.                 if($pos || is_int($pos))
  2118.                 {
  2119.                     $bd $pos+4;
  2120.                 }
  2121.                 else
  2122.                 {
  2123.                     $pos strpos($data,"\n\n");
  2124.                     if($pos || is_int($pos))
  2125.                     {
  2126.                         $bd $pos+2;
  2127.                     }
  2128.                     else
  2129.                     {
  2130.                         // No separation between response headers and body: fault?
  2131.                         $bd 0;
  2132.                     }
  2133.                 }
  2134.                 // be tolerant to line endings, and extra empty lines
  2135.                 $ar split("\r?\n"trim(substr($data0$pos)));
  2136.                 while(list(,$line@each($ar))
  2137.                 {
  2138.                     // take care of multi-line headers and cookies
  2139.                     $arr explode(':',$line,2);
  2140.                     if(count($arr1)
  2141.                     {
  2142.                         $header_name strtolower(trim($arr[0]));
  2143.                         /// @todo some other headers (the ones that allow a CSV list of values)
  2144.                         /// do allow many values to be passed using multiple header lines.
  2145.                         /// We should add content to $GLOBALS['_xh']['headers'][$header_name]
  2146.                         /// instead of replacing it for those...
  2147.                         if ($header_name == 'set-cookie' || $header_name == 'set-cookie2')
  2148.                         {
  2149.                             if ($header_name == 'set-cookie2')
  2150.                             {
  2151.                                 // version 2 cookies:
  2152.                                 // there could be many cookies on one line, comma separated
  2153.                                 $cookies explode(','$arr[1]);
  2154.                             }
  2155.                             else
  2156.                             {
  2157.                                 $cookies array($arr[1]);
  2158.                             }
  2159.                             foreach ($cookies as $cookie)
  2160.                             {
  2161.                                 // glue together all received cookies, using a comma to separate them
  2162.                                 // (same as php does with getallheaders())
  2163.                                 if (isset($GLOBALS['_xh']['headers'][$header_name]))
  2164.                                     $GLOBALS['_xh']['headers'][$header_name.= ', ' trim($cookie);
  2165.                                 else
  2166.                                     $GLOBALS['_xh']['headers'][$header_nametrim($cookie);
  2167.                                 // parse cookie attributes, in case user wants to coorectly honour then
  2168.                                 // feature creep: only allow rfc-compliant cookie attributes?
  2169.                                 $cookie explode(';'$cookie);
  2170.                                 foreach ($cookie as $pos => $val)
  2171.                                 {
  2172.                                     $val explode('='$val2);
  2173.                                     $tag trim($val[0]);
  2174.                                     $val trim(@$val[1]);
  2175.                                     /// @todo with version 1 cookies, we should strip leading and trailing " chars
  2176.                                     if ($pos == 0)
  2177.                                     {
  2178.                                         $cookiename $tag;
  2179.                                         $GLOBALS['_xh']['cookies'][$tagarray();
  2180.                                         $GLOBALS['_xh']['cookies'][$cookiename]['value'urldecode($val);
  2181.                                     }
  2182.                                     else
  2183.                                     {
  2184.                                         $GLOBALS['_xh']['cookies'][$cookiename][$tag$val;
  2185.                                     }
  2186.                                 }
  2187.                             }
  2188.                         }
  2189.                         else
  2190.                         {
  2191.                             $GLOBALS['_xh']['headers'][$header_nametrim($arr[1]);
  2192.                         }
  2193.                     }
  2194.                     elseif(isset($header_name))
  2195.                     {
  2196.                         ///    @todo version1 cookies might span multiple lines, thus breaking the parsing above
  2197.                         $GLOBALS['_xh']['headers'][$header_name.= ' ' trim($line);
  2198.                     }
  2199.                 }
  2200.                 // rebuild full cookie set
  2201.                 /*if (isset($GLOBALS['_xh']['headers']['set-cookie']))
  2202.                 {
  2203.                     $cookies = array();
  2204.                     $received = explode(';', $GLOBALS['_xh']['headers']['set-cookie']);
  2205.                     foreach($received as $cookie)
  2206.                     {
  2207.                         list($name, $value) = explode('=', $cookie);
  2208.                         $name = trim($name);
  2209.                         $value = trim($value);
  2210.                         // these values are in fact attributes
  2211.                         if ($name != 'Comment' && $name != 'Comment' && $name != 'Comment' && $name != 'Comment' && $name != 'Comment' && $name != 'Comment')
  2212.                         {
  2213.                             $cookies[$name] = $value;
  2214.                         }
  2215.                     }
  2216.                 }*/
  2217.  
  2218.                 $data substr($data$bd);
  2219.  
  2220.                 if($this->debug && count($GLOBALS['_xh']['headers']))
  2221.                 {
  2222.                     print '<PRE>';
  2223.                     foreach($GLOBALS['_xh']['headers'as $header => $value)
  2224.                     {
  2225.                         print "HEADER$header$value\n";
  2226.                     }
  2227.                     foreach($GLOBALS['_xh']['cookies'as $header => $value)
  2228.                     {
  2229.                         print "COOKIE$header={$value['value']}\n";
  2230.                     }
  2231.                     print "</PRE>\n";
  2232.                 }
  2233.  
  2234.                 // if CURL was used for the call, http headers have been processed,
  2235.                 // and dechunking + reinflating have been carried out
  2236.                 if(!$headers_processed)
  2237.                 {
  2238.                     // Decode chunked encoding sent by http 1.1 servers
  2239.                     if(isset($GLOBALS['_xh']['headers']['transfer-encoding']&& $GLOBALS['_xh']['headers']['transfer-encoding'== 'chunked')
  2240.                     {
  2241.                         if(!$data decode_chunked($data))
  2242.                         {
  2243.                             error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to rebuild the chunked data received from server');
  2244.                             $r new xmlrpcresp(0$GLOBALS['xmlrpcerr']['dechunk_fail']$GLOBALS['xmlrpcstr']['dechunk_fail']);
  2245.                             return $r;
  2246.                         }
  2247.                     }
  2248.  
  2249.                     // Decode gzip-compressed stuff
  2250.                     // code shamelessly inspired from nusoap library by Dietrich Ayala
  2251.                     if(isset($GLOBALS['_xh']['headers']['content-encoding']))
  2252.                     {
  2253.                         if($GLOBALS['_xh']['headers']['content-encoding'== 'deflate' || $GLOBALS['_xh']['headers']['content-encoding'== 'gzip')
  2254.                         {
  2255.                             // if decoding works, use it. else assume data wasn't gzencoded
  2256.                             if(function_exists('gzinflate'))
  2257.                             {
  2258.                                 if($GLOBALS['_xh']['headers']['content-encoding'== 'deflate' && $degzdata @gzinflate($data))
  2259.                                 {
  2260.                                     $data $degzdata;
  2261.                                     if($this->debug)
  2262.                                     print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" htmlentities($data"\n---END---</PRE>";
  2263.                                 }
  2264.                                 elseif($GLOBALS['_xh']['headers']['content-encoding'== 'gzip' && $degzdata @gzinflate(substr($data10)))
  2265.                                 {
  2266.                                     $data $degzdata;
  2267.                                     if($this->debug)
  2268.                                     print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" htmlentities($data"\n---END---</PRE>";
  2269.                                 }
  2270.                                 else
  2271.                                 {
  2272.                                     error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to decode the deflated data received from server');
  2273.                                     $r new xmlrpcresp(0$GLOBALS['xmlrpcerr']['decompress_fail']$GLOBALS['xmlrpcstr']['decompress_fail']);
  2274.                                     return $r;
  2275.                                 }
  2276.                             }
  2277.                             else
  2278.                             {
  2279.                                 error_log('XML-RPC: xmlrpcmsg::parseResponse: the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
  2280.                                 $r new xmlrpcresp(0$GLOBALS['xmlrpcerr']['cannot_decompress']$GLOBALS['xmlrpcstr']['cannot_decompress']);
  2281.                                 return $r;
  2282.                             }
  2283.                         }
  2284.                     }
  2285.                 // end of 'if needed, de-chunk, re-inflate response'
  2286.  
  2287.                 // real stupid hack to avoid PHP 4 complaining about returning NULL by ref
  2288.                 $r null;
  2289.                 $r =$r;
  2290.                 return $r;
  2291.         }
  2292.  
  2293.         /*
  2294.         * @param string $data the xmlrpc response, eventually including http headers
  2295.         * @param bool   $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and conseuqent decoding
  2296.         * @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
  2297.         * @access private
  2298.         */
  2299.         function &parseResponse($data=''$headers_processed=false$return_type='xmlrpcvals')
  2300.         {
  2301.             //$hdrfnd = 0;
  2302.             if($this->debug)
  2303.             {
  2304.                 //by maHo, replaced htmlspecialchars with htmlentities
  2305.                 print "<PRE>---GOT---\n" htmlentities($data"\n---END---\n</PRE>";
  2306.                 $start strpos($data'<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
  2307.                 if ($start)
  2308.                 {
  2309.                     $start += strlen('<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
  2310.                     $end strpos($data'-->'$start);
  2311.                     $comments substr($data$start$end-$start);
  2312.                     print "<PRE>---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n""\n\t"base64_decode($comments)))."\n---END---\n</PRE>";
  2313.                 }
  2314.             }
  2315.  
  2316.             if($data == '')
  2317.             {
  2318.                 error_log('XML-RPC: xmlrpcmsg::parseResponse: no response received from server.');
  2319.                 $r new xmlrpcresp(0$GLOBALS['xmlrpcerr']['no_data']$GLOBALS['xmlrpcstr']['no_data']);
  2320.                 return $r;
  2321.             }
  2322.  
  2323.             $GLOBALS['_xh']=array();
  2324.  
  2325.             // parse the HTTP headers of the response, if present, and separate them from data
  2326.             if(ereg("^HTTP",$data))
  2327.             {
  2328.                 $r =$this->parseResponseHeaders($data$headers_processed);
  2329.                 if ($r)
  2330.                 {
  2331.                     return $r;
  2332.                 }
  2333.             }
  2334.             else
  2335.             {
  2336.                 $GLOBALS['_xh']['headers'array();
  2337.                 $GLOBALS['_xh']['cookies'array();
  2338.             }
  2339.  
  2340.  
  2341.             // be tolerant of extra whitespace in response body
  2342.             $data trim($data);
  2343.  
  2344.             /// @todo return an error msg if $data=='' ?
  2345.  
  2346.             // be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts)
  2347.             // idea from Luca Mariano <[email protected]> originally in PEARified version of the lib
  2348.             $bd false;
  2349.             // Poor man's version of strrpos for php 4...
  2350.             $pos strpos($data'</methodResponse>');
  2351.             while($pos || is_int($pos))
  2352.             {
  2353.                 $bd $pos+17;
  2354.                 $pos strpos($data'</methodResponse>'$bd);
  2355.             }
  2356.             if($bd)
  2357.             {
  2358.                 $data substr($data0$bd);
  2359.             }
  2360.  
  2361.             // if user wants back raw xml, give it to him
  2362.             if ($return_type == 'xml')
  2363.             {
  2364.                 $r new xmlrpcresp($data0'''xml');
  2365.                 $r->hdrs $GLOBALS['_xh']['headers'];
  2366.                 $r->_cookies $GLOBALS['_xh']['cookies'];
  2367.                 return $r;
  2368.             }
  2369.  
  2370.             // try to 'guestimate' the character encoding of the received response
  2371.             $resp_encoding guess_encoding(@$GLOBALS['_xh']['headers']['content-type']$data);
  2372.  
  2373.             $GLOBALS['_xh']['stack'array();
  2374.             $GLOBALS['_xh']['valuestack'array();
  2375.             $GLOBALS['_xh']['isf']=0;
  2376.             $GLOBALS['_xh']['isf_reason']='';
  2377.             $GLOBALS['_xh']['ac']='';
  2378.             $GLOBALS['_xh']['qt']='';
  2379.  
  2380.             // if response charset encoding is not known / supported, try to use
  2381.             // the default encoding and parse the xml anyway, but log a warning...
  2382.             if (!in_array($resp_encodingarray('UTF-8''ISO-8859-1''US-ASCII')))
  2383.             // the following code might be better for mb_string enabled installs, but
  2384.             // makes the lib about 200% slower...
  2385.             //if (!is_valid_charset($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
  2386.             {
  2387.                 error_log('XML-RPC: xmlrpcmsg::parseResponse: invalid charset encoding of received response: '.$resp_encoding);
  2388.                 $resp_encoding $GLOBALS['xmlrpc_defencoding'];
  2389.             }
  2390.             $parser xml_parser_create($resp_encoding);
  2391.             xml_parser_set_option($parserXML_OPTION_CASE_FOLDINGtrue);
  2392.             // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
  2393.             // the xml parser to give us back data in the expected charset
  2394.             xml_parser_set_option($parserXML_OPTION_TARGET_ENCODING$GLOBALS['xmlrpc_internalencoding']);
  2395.  
  2396.             if ($return_type == 'phpvals')
  2397.             {
  2398.                 xml_set_element_handler($parser'xmlrpc_se''xmlrpc_ee_fast');
  2399.             }
  2400.             else
  2401.             {
  2402.                 xml_set_element_handler($parser'xmlrpc_se''xmlrpc_ee');
  2403.             }
  2404.  
  2405.             xml_set_character_data_handler($parser'xmlrpc_cd');
  2406.             xml_set_default_handler($parser'xmlrpc_dh');
  2407.  
  2408.             // first error check: xml not well formed
  2409.             if(!xml_parse($parser$datasizeof($data)))
  2410.             {
  2411.                 // thanks to Peter Kocks <[email protected]>
  2412.                 if((xml_get_current_line_number($parser)) == 1)
  2413.                 {
  2414.                     $errstr 'XML error at line 1, check URL';
  2415.                 }
  2416.                 else
  2417.                 {
  2418.                     $errstr sprintf('XML error: %s at line %d, column %d',
  2419.                         xml_error_string(xml_get_error_code($parser)),
  2420.                         xml_get_current_line_number($parser)xml_get_current_column_number($parser));
  2421.                 }
  2422.                 error_log($errstr);
  2423.                 $r=&new xmlrpcresp(0$GLOBALS['xmlrpcerr']['invalid_return']$GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')');
  2424.                 xml_parser_free($parser);
  2425.                 if($this->debug)
  2426.                 {
  2427.                     print $errstr;
  2428.                 }
  2429.                 $r->hdrs $GLOBALS['_xh']['headers'];
  2430.                 $r->_cookies $GLOBALS['_xh']['cookies'];
  2431.                 return $r;
  2432.             }
  2433.             xml_parser_free($parser);
  2434.             // second error check: xml well formed but not xml-rpc compliant
  2435.             if ($GLOBALS['_xh']['isf'1)
  2436.             {
  2437.                 if ($this->debug)
  2438.                 {
  2439.                     /// @todo echo something for user?
  2440.                 }
  2441.  
  2442.                 $r new xmlrpcresp(0$GLOBALS['xmlrpcerr']['invalid_return'],
  2443.                 $GLOBALS['xmlrpcstr']['invalid_return'' ' $GLOBALS['_xh']['isf_reason']);
  2444.             }
  2445.             // third error check: parsing of the response has somehow gone boink.
  2446.             // NB: shall we omit this check, since we trust the parsing code?
  2447.             elseif ($return_type == 'xmlrpcvals' && !is_object($GLOBALS['_xh']['value']))
  2448.             {
  2449.                 // something odd has happened
  2450.                 // and it's time to generate a client side error
  2451.                 // indicating something odd went on
  2452.                 $r=&new xmlrpcresp(0$GLOBALS['xmlrpcerr']['invalid_return'],
  2453.                 $GLOBALS['xmlrpcstr']['invalid_return']);
  2454.             }
  2455.             else
  2456.             {
  2457.                 if ($this->debug)
  2458.                 {
  2459.                     print "<PRE>---PARSED---\n" ;
  2460.                     var_export($GLOBALS['_xh']['value']);
  2461.                     print "\n---END---</PRE>";
  2462.                 }
  2463.  
  2464.                 // note that using =& will raise an error if $GLOBALS['_xh']['st'] does not generate an object.
  2465.                 $v =$GLOBALS['_xh']['value'];
  2466.  
  2467.                 if($GLOBALS['_xh']['isf'])
  2468.                 {
  2469.                     if ($return_type == 'xmlrpcvals')
  2470.                     {
  2471.                         $errno_v $v->structmem('faultCode');
  2472.                         $errstr_v $v->structmem('faultString');
  2473.                         $errno $errno_v->scalarval();
  2474.                         $errstr $errstr_v->scalarval();
  2475.                     }
  2476.                     else
  2477.                     {
  2478.                         $errno $v['faultCode'];
  2479.                         $errstr $v['faultString'];
  2480.                     }
  2481.  
  2482.                     if($errno == 0)
  2483.                     {
  2484.                         // FAULT returned, errno needs to reflect that
  2485.                         $errno = -1;
  2486.                     }
  2487.  
  2488.                     $r new xmlrpcresp(0$errno$errstr);
  2489.                 }
  2490.                 else
  2491.                 {
  2492.                     $r=&new xmlrpcresp($v0''$return_type);
  2493.                 }
  2494.             }
  2495.  
  2496.             $r->hdrs $GLOBALS['_xh']['headers'];
  2497.             $r->_cookies $GLOBALS['_xh']['cookies'];
  2498.             return $r;
  2499.         }
  2500.     }
  2501.  
  2502.     class xmlrpcval
  2503.     {
  2504.         var $me=array();
  2505.         var $mytype=0;
  2506.         var $_php_class=null;
  2507.  
  2508.         function xmlrpcval($val=-1$type='')
  2509.         {
  2510.             //$this->me=array();
  2511.             //$this->mytype=0;
  2512.             if($val!==-|| $type!='')
  2513.             {
  2514.                 if($type=='')
  2515.                 {
  2516.                     $type='string';
  2517.                 }
  2518.                 if($GLOBALS['xmlrpcTypes'][$type]==1)
  2519.                 {
  2520.                     $this->addScalar($val,$type);
  2521.                 }
  2522.                 elseif($GLOBALS['xmlrpcTypes'][$type]==2)
  2523.                 {
  2524.                     $this->addArray($val);
  2525.                 }
  2526.                 elseif($GLOBALS['xmlrpcTypes'][$type]==3)
  2527.                 {
  2528.                     $this->addStruct($val);
  2529.                 }
  2530.             }
  2531.         }
  2532.  
  2533.         function addScalar($val$type='string')
  2534.         {
  2535.             $typeof=@$GLOBALS['xmlrpcTypes'][$type];
  2536.             if($typeof!=1)
  2537.             {
  2538.                 error_log("XML-RPCxmlrpcval::addScalarnot a scalar type ($typeof)");
  2539.                 return 0;
  2540.             }
  2541.  
  2542.             // coerce booleans into correct values
  2543.             // NB: shall we do it for datetimes, integers and doubles, too?
  2544.             if($type==$GLOBALS['xmlrpcBoolean'])
  2545.             {
  2546.                 if(strcasecmp($val,'true')==|| $val==|| ($val==true && strcasecmp($val,'false')))
  2547.                 {
  2548.                     $val=true;
  2549.                 }
  2550.                 else
  2551.                 {
  2552.                     $val=false;
  2553.                 }
  2554.             }
  2555.  
  2556.             switch($this->mytype)
  2557.             {
  2558.                 case 1:
  2559.                     error_log('XML-RPC: xmlrpcval::addScalar: scalar xmlrpcval can have only one value');
  2560.                     return 0;
  2561.                 case 3:
  2562.                     error_log('XML-RPC: xmlrpcval::addScalar: cannot add anonymous scalar to struct xmlrpcval');
  2563.                     return 0;
  2564.                 case 2:
  2565.                     // we're adding a scalar value to an array here
  2566.                     //$ar=$this->me['array'];
  2567.                     //$ar[]=&new xmlrpcval($val, $type);
  2568.                     //$this->me['array']=$ar;
  2569.                     // Faster (?) avoid all the costly array-copy-by-val done here...
  2570.                     $this->me['array'][]=&new xmlrpcval($val$type);
  2571.                     return 1;
  2572.                 default:
  2573.                     // a scalar, so set the value and remember we're scalar
  2574.                     $this->me[$type]=$val;
  2575.                     $this->mytype=$typeof;
  2576.                     return 1;
  2577.             }
  2578.         }
  2579.  
  2580.         /// @todo add some checking for $vals to be an array of xmlrpcvals?
  2581.                 function addArray($vals)
  2582.         {
  2583.             if($this->mytype==0)
  2584.             {
  2585.                 $this->mytype=$GLOBALS['xmlrpcTypes']['array'];
  2586.                 $this->me['array']=$vals;
  2587.                 return 1;
  2588.             }
  2589.             elseif($this->mytype==2)
  2590.             {
  2591.                 // we're adding to an array here
  2592.                 $this->me['array'array_merge($this->me['array']$vals);
  2593.             }
  2594.             else
  2595.             {
  2596.                 error_log('XML-RPC: xmlrpcval::addArray: already initialized as a [' $this->kindOf(']');
  2597.                 return 0;
  2598.             }
  2599.         }
  2600.  
  2601.         /// @todo add some checking for $vals to be an array?
  2602.                 function addStruct($vals)
  2603.         {
  2604.             if($this->mytype==0)
  2605.             {
  2606.                 $this->mytype=$GLOBALS['xmlrpcTypes']['struct'];
  2607.                 $this->me['struct']=$vals;
  2608.                 return 1;
  2609.             }
  2610.             elseif($this->mytype==3)
  2611.             {
  2612.                 // we're adding to a struct here
  2613.                 $this->me['struct'array_merge($this->me['struct']$vals);
  2614.             }
  2615.             else
  2616.             {
  2617.                 error_log('XML-RPC: xmlrpcval::addStruct: already initialized as a [' $this->kindOf(']');
  2618.                 return 0;
  2619.             }
  2620.         }
  2621.  
  2622.         // poor man's version of print_r ???
  2623.         // DEPRECATED!
  2624.                 function dump($ar)
  2625.         {
  2626.             foreach($ar as $key => $val)
  2627.             {
  2628.                 echo "$key => $val<br />";
  2629.                 if($key == 'array')
  2630.                 {
  2631.                     while(list($key2$val2each($val))
  2632.                     {
  2633.                         echo "-- $key2 => $val2<br />";
  2634.                     }
  2635.                 }
  2636.             }
  2637.         }
  2638.  
  2639.         function kindOf()
  2640.         {
  2641.             switch($this->mytype)
  2642.             {
  2643.                 case 3:
  2644.                     return 'struct';
  2645.                     break;
  2646.                 case 2:
  2647.                     return 'array';
  2648.                     break;
  2649.                 case 1:
  2650.                     return 'scalar';
  2651.                     break;
  2652.                 default:
  2653.                     return 'undef';
  2654.             }
  2655.         }
  2656.  
  2657.         function serializedata($typ$val$charset_encoding='')
  2658.         {
  2659.             $rs='';
  2660.             switch(@$GLOBALS['xmlrpcTypes'][$typ])
  2661.             {
  2662.                 case 3:
  2663.                     // struct
  2664.                     if ($this->_php_class)
  2665.                     {
  2666.                         $rs.='<struct php_class="' $this->_php_class . "\">\n";
  2667.                     }
  2668.                     else
  2669.                     {
  2670.                         $rs.="<struct>\n";
  2671.                     }
  2672.                     foreach($val as $key2 => $val2)
  2673.                     {
  2674.                         $rs.="<member><name>${key2}</name>\n";
  2675.                         //$rs.=$this->serializeval($val2);
  2676.                         $rs.=$val2->serialize($charset_encoding);
  2677.                         $rs.="</member>\n";
  2678.                     }
  2679.                     $rs.='</struct>';
  2680.                     break;
  2681.                 case 2:
  2682.                     // array
  2683.                     $rs.="<array>\n<data>\n";
  2684.                     for($i=0$i<sizeof($val)$i++)
  2685.                     {
  2686.                         //$rs.=$this->serializeval($val[$i]);
  2687.                         $rs.=$val[$i]->serialize($charset_encoding);
  2688.                     }
  2689.                     $rs.="</data>\n</array>";
  2690.                     break;
  2691.                 case 1:
  2692.                     switch($typ)
  2693.                     {
  2694.                         case $GLOBALS['xmlrpcBase64']:
  2695.                             $rs.="<${typ}>base64_encode($val"</${typ}>";
  2696.                             break;
  2697.                         case $GLOBALS['xmlrpcBoolean']:
  2698.                             $rs.="<${typ}>($val '1' '0'"</${typ}>";
  2699.                             break;
  2700.                         case $GLOBALS['xmlrpcString']:
  2701.                             // G. Giunta 2005/2/13: do NOT use htmlentities, since
  2702.                             // it will produce named html entities, which are invalid xml
  2703.                             $rs.="<${typ}>xmlrpc_encode_entitites($val$GLOBALS['xmlrpc_internalencoding']$charset_encoding)"</${typ}>";
  2704.                             // $rs.="<${typ}>" . htmlentities($val). "</${typ}>";
  2705.                             break;
  2706.                         case $GLOBALS['xmlrpcInt']:
  2707.                         case $GLOBALS['xmlrpcI4']:
  2708.                             $rs.="<${typ}>".(int)$val."</${typ}>";
  2709.                             break;
  2710.                         case $GLOBALS['xmlrpcDouble']:
  2711.                             $rs.="<${typ}>".(double)$val."</${typ}>";
  2712.                             break;
  2713.                         default:
  2714.                             // no standard type value should arrive here, but provide a possibility
  2715.                             // for xmlrpcvals of unknown type...
  2716.                             $rs.="<${typ}>${val}</${typ}>";
  2717.                     }
  2718.                     break;
  2719.                 default:
  2720.                     break;
  2721.             }
  2722.             return $rs;
  2723.         }
  2724.  
  2725.         /**
  2726.         * Return xml representation of the value
  2727.         * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
  2728.         */
  2729.         function serialize($charset_encoding='')
  2730.         {
  2731.             // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
  2732.             //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
  2733.             //{
  2734.                 reset($this->me);
  2735.                 list($typ$valeach($this->me);
  2736.                 return '<value>' $this->serializedata($typ$val$charset_encoding"</value>\n";
  2737.             //}
  2738.         }
  2739.  
  2740.         // DEPRECATED
  2741.                 function serializeval($o)
  2742.         {
  2743.             // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
  2744.             //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
  2745.             //{
  2746.                 $ar=$o->me;
  2747.                 reset($ar);
  2748.                 list($typ$valeach($ar);
  2749.                 return '<value>' $this->serializedata($typ$val"</value>\n";
  2750.             //}
  2751.         }
  2752.  
  2753.         /**
  2754.         * Checks wheter a struct member with a given name is present.
  2755.         * Works only on xmlrpcvals of type struct.
  2756.         * @param string $m the name of the struct member to be looked up
  2757.         * @return boolean 
  2758.         */
  2759.         function structmemexists($m)
  2760.         {
  2761.             return array_key_exists($m$this->me['struct']);
  2762.         }
  2763.  
  2764.         /*
  2765.         * Returns the value of a given struct member (an xmlrpcval object in itself).
  2766.         * Will raise a php warning if struct member of given name does not exist
  2767.         * @param string $m the name of the struct member to be looked up
  2768.         * @return xmlrpcval
  2769.         */
  2770.         function structmem($m)
  2771.         {
  2772.             return $this->me['struct'][$m];
  2773.         }
  2774.  
  2775.         function structreset()
  2776.         {
  2777.             reset($this->me['struct']);
  2778.         }
  2779.  
  2780.         function structeach()
  2781.         {
  2782.             return each($this->me['struct']);
  2783.         }
  2784.  
  2785.         // DEPRECATED! this code looks like it is very fragile and has not been fixed
  2786.         // for a long long time. Shall we remove it for 2.0?
  2787.                 function getval()
  2788.         {
  2789.             // UNSTABLE
  2790.             reset($this->me);
  2791.             list($a,$b)=each($this->me);
  2792.             // contributed by I Sofer, 2001-03-24
  2793.             // add support for nested arrays to scalarval
  2794.             // i've created a new method here, so as to
  2795.             // preserve back compatibility
  2796.  
  2797.             if(is_array($b))
  2798.             {
  2799.                 @reset($b);
  2800.                 while(list($id,$cont@each($b))
  2801.                 {
  2802.                     $b[$id$cont->scalarval();
  2803.                 }
  2804.             }
  2805.  
  2806.             // add support for structures directly encoding php objects
  2807.             if(is_object($b))
  2808.             {
  2809.                 $t get_object_vars($b);
  2810.                 @reset($t);
  2811.                 while(list($id,$cont@each($t))
  2812.                 {
  2813.                     $t[$id$cont->scalarval();
  2814.                 }
  2815.                 @reset($t);
  2816.                 while(list($id,$cont@each($t))
  2817.                 {
  2818.                     //@eval('$b->'.$id.' = $cont;');
  2819.                     @$b->$id $cont;
  2820.                 }
  2821.             }
  2822.             // end contrib
  2823.             return $b;
  2824.         }
  2825.  
  2826.         /**
  2827.         * Returns the value of a scalar xmlrpcval
  2828.         * @return mixed 
  2829.         */
  2830.         function scalarval()
  2831.         {
  2832.             reset($this->me);
  2833.             list(,$b)=each($this->me);
  2834.             return $b;
  2835.         }
  2836.  
  2837.         /**
  2838.         * Returns the type of the xmlrpcval.
  2839.         * For integers, 'int' is always returned in place of 'i4'
  2840.         * @return string 
  2841.         */
  2842.         function scalartyp()
  2843.         {
  2844.             reset($this->me);
  2845.             list($a,$b)=each($this->me);
  2846.             if($a==$GLOBALS['xmlrpcI4'])
  2847.             {
  2848.                 $a=$GLOBALS['xmlrpcInt'];
  2849.             }
  2850.             return $a;
  2851.         }
  2852.  
  2853.         /**
  2854.         * Returns the m-th member of an xmlrpcval of struct type
  2855.         * @param integer $m the index of the value to be retrieved (zero based)
  2856.         * @return xmlrpcval 
  2857.         */
  2858.         function arraymem($m)
  2859.         {
  2860.             return $this->me['array'][$m];
  2861.         }
  2862.  
  2863.         /**
  2864.         * Returns the number of members in an xmlrpcval of array type
  2865.         * @return integer 
  2866.         */
  2867.         function arraysize()
  2868.         {
  2869.             return count($this->me['array']);
  2870.         }
  2871.  
  2872.         /**
  2873.         * Returns the number of members in an xmlrpcval of struct type
  2874.         * @return integer 
  2875.         */
  2876.         function structsize()
  2877.         {
  2878.             return count($this->me['struct']);
  2879.         }
  2880.     }
  2881.  
  2882.  
  2883.     // date helpers
  2884.         function iso8601_encode($timet$utc=0)
  2885.     {
  2886.         // return an ISO8601 encoded string
  2887.         // really, timezones ought to be supported
  2888.         // but the XML-RPC spec says:
  2889.         //
  2890.         // "Don't assume a timezone. It should be specified by the server in its
  2891.         // documentation what assumptions it makes about timezones."
  2892.         //
  2893.         // these routines always assume localtime unless
  2894.         // $utc is set to 1, in which case UTC is assumed
  2895.         // and an adjustment for locale is made when encoding
  2896.         if(!$utc)
  2897.         {
  2898.             $t=strftime("%Y%m%dT%H:%M:%S"$timet);
  2899.         }
  2900.         else
  2901.         {
  2902.             if(function_exists('gmstrftime'))
  2903.             {
  2904.                 // gmstrftime doesn't exist in some versions
  2905.                 // of PHP
  2906.                 $t=gmstrftime("%Y%m%dT%H:%M:%S"$timet);
  2907.             }
  2908.             else
  2909.             {
  2910.                 $t=strftime("%Y%m%dT%H:%M:%S"$timet-date('Z'));
  2911.             }
  2912.         }
  2913.         return $t;
  2914.     }
  2915.  
  2916.     function iso8601_decode($idate$utc=0)
  2917.     {
  2918.         // return a timet in the localtime, or UTC
  2919.         $t=0;
  2920.         if(ereg("([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})"$idate$regs))
  2921.         {
  2922.             if($utc)
  2923.             {
  2924.                 $t=gmmktime($regs[4]$regs[5]$regs[6]$regs[2]$regs[3]$regs[1]);
  2925.             }
  2926.             else
  2927.             {
  2928.                 $t=mktime($regs[4]$regs[5]$regs[6]$regs[2]$regs[3]$regs[1]);
  2929.             }
  2930.         }
  2931.         return $t;
  2932.     }
  2933.  
  2934.     /**
  2935.     * Takes an xmlrpc value in PHP xmlrpcval object format
  2936.     * and translates it into native PHP types.
  2937.     * Works with xmlrpc message objects as input, too.
  2938.     *
  2939.     * @author Dan Libby ([email protected])
  2940.     *
  2941.     * @param  xmlrpcval $xmlrpc_val 
  2942.     * @param  array     $options    if 'decode_php_objs' is set in the options array, xmlrpc structs can be decoded into php objects
  2943.     * @return mixed 
  2944.     */
  2945.     function php_xmlrpc_decode($xmlrpc_val$options=array())
  2946.     {
  2947.         switch($xmlrpc_val->kindOf())
  2948.         {
  2949.             case 'scalar':
  2950.                 return $xmlrpc_val->scalarval();
  2951.             case 'array':
  2952.                 $size $xmlrpc_val->arraysize();
  2953.                 $arr array();
  2954.                 for($i 0$i $size$i++)
  2955.                 {
  2956.                     $arr[php_xmlrpc_decode($xmlrpc_val->arraymem($i)$options);
  2957.                 }
  2958.                 return $arr;
  2959.             case 'struct':
  2960.                 $xmlrpc_val->structreset();
  2961.                 // If user said so, try to rebuild php objects for specific struct vals.
  2962.                 /// @todo should we raise a warning for class not found?
  2963.                 // shall we check for proper subclass of xmlrpcval instead of
  2964.                 // presence of _php_class to detect what we can do?
  2965.                 if (in_array('decode_php_objs'$options&& $xmlrpc_val->_php_class != ''
  2966.                     && class_exists($xmlrpc_val->_php_class))
  2967.                 {
  2968.                     $obj @new $xmlrpc_val->_php_class;
  2969.                     while(list($key,$value)=$xmlrpc_val->structeach())
  2970.                     {
  2971.                         $obj->$key php_xmlrpc_decode($value$options);
  2972.                     }
  2973.                     return $obj;
  2974.                 }
  2975.                 else
  2976.                 {
  2977.                     $arr array();
  2978.                     while(list($key,$value)=$xmlrpc_val->structeach())
  2979.                     {
  2980.                         $arr[$keyphp_xmlrpc_decode($value$options);
  2981.                     }
  2982.                     return $arr;
  2983.                 }
  2984.             case 'msg':
  2985.                 $paramcount $xmlrpc_val->getNumParams();
  2986.                 $arr array();
  2987.                 for($i 0$i $paramcount$i++)
  2988.                 {
  2989.                     $arr[php_xmlrpc_decode($xmlrpc_val->getParam($i));
  2990.                 }
  2991.                 return $arr;
  2992.             }
  2993.     }
  2994.  
  2995.     if(function_exists('xmlrpc_decode'))
  2996.     {
  2997.         define('XMLRPC_EPI_ENABLED','1');
  2998.     }
  2999.     else
  3000.     {
  3001.         define('XMLRPC_EPI_ENABLED','0');
  3002.     }
  3003.  
  3004.     /**
  3005.     * Takes native php types and encodes them into xmlrpc PHP object format.
  3006.     * It will not re-encode xmlrpcval objects.
  3007.     * Feature creep -- could support more types via optional type argument
  3008.     * (string => datetime support has been added, ??? => base64 not yet)
  3009.     *
  3010.     * @author Dan Libby ([email protected])
  3011.     *
  3012.     * @param mixed $php_val the value to be converted into an xmlrpcval object
  3013.     * @param array $options    can include 'encode_php_objs' and 'auto_dates'
  3014.     * @return xmlrpcval 
  3015.     */
  3016.     function &php_xmlrpc_encode($php_val$options=array())
  3017.     {
  3018.         $type gettype($php_val);
  3019.         $xmlrpc_val new xmlrpcval;
  3020.  
  3021.         switch($type)
  3022.         {
  3023.             case 'array':
  3024.                 // PHP arrays can be encoded to either xmlrpc structs or arrays,
  3025.                 // depending on wheter they are hashes or plain 0..n integer indexed
  3026.                 // A shorter one-liner would be
  3027.                 // $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
  3028.                 // but execution time skyrockets!
  3029.                 $j 0;
  3030.                 $arr array();
  3031.                 $ko false;
  3032.                 foreach($php_val as $key => $val)
  3033.                 {
  3034.                     $arr[$key=php_xmlrpc_encode($val$options);
  3035.                     if(!$ko && $key !== $j)
  3036.                     {
  3037.                         $ko true;
  3038.                     }
  3039.                     $j++;
  3040.                 }
  3041.                 if($ko)
  3042.                 {
  3043.                     $xmlrpc_val->addStruct($arr);
  3044.                 }
  3045.                 else
  3046.                 {
  3047.                     $xmlrpc_val->addArray($arr);
  3048.                 }
  3049.                 break;
  3050.             case 'object':
  3051.                 if(is_a($php_val'xmlrpcval'))
  3052.                 {
  3053.                     $xmlrpc_val $php_val;
  3054.                 }
  3055.                 else
  3056.                 {
  3057.                     $arr array();
  3058.                     while(list($k,$veach($php_val))
  3059.                     {
  3060.                         $arr[$kphp_xmlrpc_encode($v$options);
  3061.                     }
  3062.                     $xmlrpc_val->addStruct($arr);
  3063.                     if (in_array('encode_php_objs'$options))
  3064.                     {
  3065.                         // let's save original class name into xmlrpcval:
  3066.                         // might be useful later on...
  3067.                         $xmlrpc_val->_php_class get_class($php_val);
  3068.                     }
  3069.                 }
  3070.                 break;
  3071.             case 'integer':
  3072.                 $xmlrpc_val->addScalar($php_val$GLOBALS['xmlrpcInt']);
  3073.                 break;
  3074.             case 'double':
  3075.                 $xmlrpc_val->addScalar($php_val$GLOBALS['xmlrpcDouble']);
  3076.                 break;
  3077.             case 'string':
  3078.                 if (in_array('auto_dates'$options&& ereg("^[0-9]{8}\T{1}[0-9]{2}\:[0-9]{2}\:[0-9]{2}$"$php_val))
  3079.                     $xmlrpc_val->addScalar($php_val$GLOBALS['xmlrpcDateTime']);
  3080.                 else
  3081.                     $xmlrpc_val->addScalar($php_val$GLOBALS['xmlrpcString']);
  3082.                 break;
  3083.                 // <G_Giunta_2001-02-29>
  3084.                 // Add support for encoding/decoding of booleans, since they are supported in PHP
  3085.             case 'boolean':
  3086.                 $xmlrpc_val->addScalar($php_val$GLOBALS['xmlrpcBoolean']);
  3087.                 break;
  3088.                 // </G_Giunta_2001-02-29>
  3089.             // catch "resource", "NULL", "user function", "unknown type"
  3090.             //case 'unknown type':
  3091.             default:
  3092.                 // giancarlo pinerolo <[email protected]>
  3093.                 // it has to return
  3094.                 // an empty object in case (which is already
  3095.                 // at this point), not a boolean.
  3096.                 break;
  3097.             }
  3098.             return $xmlrpc_val;
  3099.     }
  3100.  
  3101.     /**
  3102.     * decode a string that is encoded w/ "chunked" transfer encoding
  3103.     * as defined in rfc2068 par. 19.4.6
  3104.     * code shamelessly stolen from nusoap library by Dietrich Ayala
  3105.     *
  3106.     * @param   string $buffer the string to be decoded
  3107.     * @return  string 
  3108.     */
  3109.     function decode_chunked($buffer)
  3110.     {
  3111.         // length := 0
  3112.         $length 0;
  3113.         $new '';
  3114.  
  3115.         // read chunk-size, chunk-extension (if any) and crlf
  3116.         // get the position of the linebreak
  3117.         $chunkend strpos($buffer,"\r\n"2;
  3118.         $temp substr($buffer,0,$chunkend);
  3119.         $chunk_size hexdectrim($temp) );
  3120.         $chunkstart $chunkend;
  3121.         // while(chunk-size > 0) {
  3122.         while($chunk_size 0)
  3123.         {
  3124.             $chunkend strpos($buffer"\r\n"$chunkstart $chunk_size);
  3125.  
  3126.             // just in case we got a broken connection
  3127.             if($chunkend == false)
  3128.             {
  3129.                 $chunk substr($buffer,$chunkstart);
  3130.                 // append chunk-data to entity-body
  3131.                 $new .= $chunk;
  3132.                 $length += strlen($chunk);
  3133.                 break;
  3134.             }
  3135.  
  3136.             // read chunk-data and crlf
  3137.             $chunk substr($buffer,$chunkstart,$chunkend-$chunkstart);
  3138.             // append chunk-data to entity-body
  3139.             $new .= $chunk;
  3140.             // length := length + chunk-size
  3141.             $length += strlen($chunk);
  3142.             // read chunk-size and crlf
  3143.             $chunkstart $chunkend 2;
  3144.  
  3145.             $chunkend strpos($buffer,"\r\n",$chunkstart)+2;
  3146.             if($chunkend == false)
  3147.             {
  3148.                 break//just in case we got a broken connection
  3149.             }
  3150.             $temp substr($buffer,$chunkstart,$chunkend-$chunkstart);
  3151.             $chunk_size hexdectrim($temp) );
  3152.             $chunkstart $chunkend;
  3153.         }
  3154.         return $new;
  3155.     }
  3156.  
  3157.     /**
  3158.     * Given a string defining a php type or phpxmlrpc type (loosely defined: strings
  3159.     * accepted come from javadoc blocks), return corresponding phpxmlrpc type.
  3160.     * NB: for php 'resource' types returns empty string, since resources cannot be serialized;
  3161.     * for php class names returns 'struct', since php objects can be serialized as xmlrpc structs
  3162.     * @param string $phptype 
  3163.     * @return string 
  3164.     */
  3165.     function php_2_xmlrpc_type($phptype)
  3166.     {
  3167.         switch(strtolower($phptype))
  3168.         {
  3169.             case 'string':
  3170.                 return $GLOBALS['xmlrpcString'];
  3171.             case 'integer':
  3172.             case $GLOBALS['xmlrpcInt']// 'int'
  3173.             case $GLOBALS['xmlrpcI4']:
  3174.                 return $GLOBALS['xmlrpcInt'];
  3175.             case 'double':
  3176.                 return $GLOBALS['xmlrpcDouble'];
  3177.             case 'boolean':
  3178.                 return $GLOBALS['xmlrpcBoolean'];
  3179.             case 'array':
  3180.                 return $GLOBALS['xmlrpcArray'];
  3181.             case 'object':
  3182.                 return $GLOBALS['xmlrpcStruct'];
  3183.             case $GLOBALS['xmlrpcBase64']:
  3184.             case $GLOBALS['xmlrpcStruct']:
  3185.                 return strtolower($phptype);
  3186.             case 'resource':
  3187.                 return '';
  3188.             default:
  3189.                 if(class_exists($phptype))
  3190.                 {
  3191.                     return $GLOBALS['xmlrpcStruct'];
  3192.                 }
  3193.                 else
  3194.                 {
  3195.                     // unknown: might be any xmlrpc type
  3196.                     return $GLOBALS['xmlrpcValue'];
  3197.                 }
  3198.         }
  3199.     }
  3200.  
  3201.     /**
  3202.     * Given a user-defined PHP function, create a PHP 'wrapper' function that can
  3203.     * be exposed as xmlrpc method from an xmlrpc_server object and called from remote
  3204.     * clients.
  3205.     *
  3206.     * Since php is a typeless language, to infer types of input and output parameters,
  3207.     * it relies on parsing the javadoc-style comment block associated with the given
  3208.     * function. Usage of xmlrpc native types (such as datetime.dateTime.iso8601 and base64)
  3209.     * in the @param tag is also allowed, if you need the php function to receive/send
  3210.     * data in that particular format (note that base64 enncoding/decoding is transparently
  3211.     * carried out by the lib, while datetime vals are passed around as strings)
  3212.     *
  3213.     * Known limitations:
  3214.     * - requires PHP 5.0.3 +
  3215.     * - only works for user-defined functions, not for PHP internal functions
  3216.     *   (reflection does not support retrieving number/type of params for those)
  3217.     * - functions returning php objects will generate special xmlrpc responses:
  3218.     *   when the xmlrpc decoding of those responses is carried out by this same lib, using
  3219.     *   the appropriate param in php_xmlrpc_decode, the php objects will be rebuilt.
  3220.     *   In short: php objects can be serialized, too (except for their resource members),
  3221.     *   using this function.
  3222.     *   Other libs might choke on the very same xml that will be generated in this case
  3223.     *   (i.e. it has a nonstandard attribute on struct element tags)
  3224.     * - usage of javadoc @param tags using param names in a different order from the
  3225.     *   function prototype is not considered valid (to be fixed?)
  3226.     *
  3227.     * Note that since rel. 2.0RC3 the preferred method to have the server call 'standard'
  3228.     * php functions (ie. functions not expecting a single xmlrpcmsg obj as parameter)
  3229.     * is by making use of the functions_parameters_type class member.
  3230.     *
  3231.     * @param string $funcname the name of the PHP user function to be exposed as xmlrpc method; array($obj, 'methodname') might be ok too, in the future...
  3232.     * @return false on error, or an array containing the name of the new php function,
  3233.     *          its signature and docs, to be used in the server dispatch map
  3234.     *
  3235.     * @todo decide how to deal with params passed by ref: bomb out or allow?
  3236.     * @todo finish using javadoc info to build method sig if all params are named but out of order
  3237.     * @done switch to some automagic object encoding scheme
  3238.     * @todo add a check for params of 'resource' type
  3239.     * @todo add some trigger_errors when returning false?
  3240.     * @todo what to do when the PHP function returns NULL? we are currently returning bogus responses!!!
  3241.     */
  3242.     function wrap_php_function($funcname$newfuncname='')
  3243.     {
  3244.         if(version_compare(phpversion()'5.0.3'== -1)
  3245.         {
  3246.             // up to php 5.0.3 some useful reflection methods were missing
  3247.             return false;
  3248.         }
  3249.         if((is_array($funcname&& !method_exists($funcname[0]$funcname[1])) || !function_exists($funcname))
  3250.         {
  3251.             return false;
  3252.         }
  3253.         else
  3254.         {
  3255.             // determine name of new php function
  3256.             if($newfuncname == '')
  3257.             {
  3258.                 if(is_array($funcname))
  3259.                 {
  3260.                     $xmlrpcfuncname "xmlrpc_".implode('_'$funcname);
  3261.                 }
  3262.                 else
  3263.                 {
  3264.                     $xmlrpcfuncname "xmlrpc_$funcname";
  3265.                 }
  3266.             }
  3267.             else
  3268.             {
  3269.                 $xmlrpcfuncname $newfuncname;
  3270.             }
  3271.             while(function_exists($xmlrpcfuncname))
  3272.             {
  3273.                 $xmlrpcfuncname .= 'x';
  3274.             }
  3275.             $code "function $xmlrpcfuncname(\$msg{\n";
  3276.  
  3277.             // start to introspect PHP code
  3278.             $func new ReflectionFunction($funcname);
  3279.             if($func->isInternal())
  3280.             {
  3281.                 // Note: from PHP 5.1.0 onward, we will possibly be able to use invokeargs
  3282.                 // instead of getparameters to fully reflect internal php functions ?
  3283.                 return false;
  3284.             }
  3285.  
  3286.             // retrieve parameter names, types and description from javadoc comments
  3287.  
  3288.             // function description
  3289.             $desc '';
  3290.             // type of return val: by default 'any'
  3291.             $returns $GLOBALS['xmlrpcValue'];
  3292.             // type + name of function parameters
  3293.             $paramDocs array();
  3294.  
  3295.             $docs $func->getDocComment();
  3296.             if($docs != '')
  3297.             {
  3298.                 $docs explode("\n"$docs);
  3299.                 $i 0;
  3300.                 foreach($docs as $doc)
  3301.                 {
  3302.                     $doc trim($doc" \r\t/*");
  3303.                     if(strlen($doc&& strpos($doc'@'!== && !$i)
  3304.                     {
  3305.                         if($desc)
  3306.                         {
  3307.                             $desc .= "\n";
  3308.                         }
  3309.                         $desc .= $doc;
  3310.                     }
  3311.                     elseif(strpos($doc'@param'=== 0)
  3312.                     {
  3313.                         // syntax: @param type [$name] desc
  3314.                         if(preg_match('/@param\s+(\S+)(\s+\$\S+)?\s+(.+)/'$doc$matches))
  3315.                         {
  3316.                             if(strpos($matches[1]'|'))
  3317.                             {
  3318.                                 //$paramDocs[$i]['type'] = explode('|', $matches[1]);
  3319.                                 $paramDocs[$i]['type''mixed';
  3320.                             }
  3321.                             else
  3322.                             {
  3323.                                 $paramDocs[$i]['type'$matches[1];
  3324.                             }
  3325.                             $paramDocs[$i]['name'trim($matches[2]);
  3326.                             $paramDocs[$i]['doc'$matches[3];
  3327.                         }
  3328.                         $i++;
  3329.                     }
  3330.                     elseif(strpos($doc'@return'=== 0)
  3331.                     {
  3332.                         $returns preg_split("/\s+/"$doc);
  3333.                         if(isset($returns[1]))
  3334.                         {
  3335.                             $returns php_2_xmlrpc_type($returns[1]);
  3336.                         }
  3337.                     }
  3338.                 }
  3339.             }
  3340.  
  3341.             // start introspection of actual function prototype and building of PHP code
  3342.             // to be eval'd
  3343.             $params $func->getParameters();
  3344.  
  3345.             $innercode '';
  3346.             $i 0;
  3347.             $parsvariations array();
  3348.             $pars array();
  3349.             $pnum count($params);
  3350.             foreach($params as $param)
  3351.             {
  3352.                 if (isset($paramDocs[$i]['name']&& $paramDocs[$i]['name'&& strtolower($paramDocs[$i]['name']!= '$'.strtolower($param->getName()))
  3353.                 {
  3354.                     // param name from phpdoc info does not match param definition!
  3355.                     $paramDocs[$i]['type''mixed';
  3356.                 }
  3357.  
  3358.                 if($param->isOptional())
  3359.                 {
  3360.                     // this particular parameter is optional. save as valid previous list of parameters
  3361.                     $innercode .= "if (\$paramcount > $i{\n";
  3362.                     $parsvariations[$pars;
  3363.                 }
  3364.                 $innercode .= "\$p$i = \$msg->getParam($i);\n";
  3365.                 $innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_xmlrpc_decode(\$p$i);\n";
  3366.                 $pars["\$p$i";
  3367.                 $i++;
  3368.                 if($param->isOptional())
  3369.                 {
  3370.                     $innercode .= "}\n";
  3371.                 }
  3372.                 if($i == $pnum)
  3373.                 {
  3374.                     // last allowed parameters combination
  3375.                     $parsvariations[$pars;
  3376.                 }
  3377.             }
  3378.  
  3379.             $sigs array();
  3380.             if(count($parsvariations== 0)
  3381.             {
  3382.                 // only known good synopsis = no parameters
  3383.                 $parsvariations[array();
  3384.                 $minpars 0;
  3385.             }
  3386.             else
  3387.             {
  3388.                 $minpars count($parsvariations[0]);
  3389.             }
  3390.  
  3391.             if($minpars)
  3392.             {
  3393.                 // add to code the check for min params number
  3394.                 // NB: this check needs to be done BEFORE decoding param values
  3395.                 $innercode "\$paramcount = \$msg->getNumParams();\n" .
  3396.                 "if (\$paramcount < $minparsreturn new xmlrpcresp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}');\n$innercode;
  3397.             }
  3398.             else
  3399.             {
  3400.                 $innercode "\$paramcount = \$msg->getNumParams();\n" $innercode;
  3401.             }
  3402.  
  3403.             $innercode .= "\$np = false;";
  3404.             foreach($parsvariations as $pars)
  3405.             {
  3406.                 $innercode .= "if (\$paramcount == " count($pars") \$retval = $funcname(implode(','$pars"); else\n";
  3407.                 // build a 'generic' signature (only use an appropriate return type)
  3408.                 $sig array($returns);
  3409.                 for($i=0$i count($pars)$i++)
  3410.                 {
  3411.                     if (isset($paramDocs[$i]['type']))
  3412.                     {
  3413.                         $sig[php_2_xmlrpc_type($paramDocs[$i]['type']);
  3414.                     }
  3415.                     else
  3416.                     {
  3417.                         $sig[$GLOBALS['xmlrpcValue'];
  3418.                     }
  3419.                 }
  3420.                 $sigs[$sig;
  3421.             }
  3422.             $innercode .= "\$np = true;\n";
  3423.             $innercode .= "if (\$npreturn new xmlrpcresp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}'); else\n";
  3424.             //$innercode .= "if (\$_xmlrpcs_error_occurred) return new xmlrpcresp(0, $GLOBALS['xmlrpcerr']user, \$_xmlrpcs_error_occurred); else\n";
  3425.             if($returns == $GLOBALS['xmlrpcDateTime'|| $returns == $GLOBALS['xmlrpcBase64'])
  3426.             {
  3427.                 $innercode .= "return new xmlrpcresp(new xmlrpcval(\$retval, '$returns'));";
  3428.             }
  3429.             else
  3430.             {
  3431.                 $innercode .= "return new xmlrpcresp(php_xmlrpc_encode(\$retval, array('encode_php_objs')));";
  3432.             }
  3433.             // shall we exclude functions returning by ref?
  3434.             // if($func->returnsReference())
  3435.             //  return false;
  3436.             $code $code $innercode "\n}\n \$allOK=1;";
  3437.             //print_r($code);
  3438.             $allOK 0;
  3439.             eval($code);
  3440.             // alternative
  3441.             //$xmlrpcfuncname = create_function('$m', $innercode);
  3442.  
  3443.             if(!$allOK)
  3444.             {
  3445.                 return false;
  3446.             }
  3447.  
  3448.             /// @todo examine if $paramDocs matches $parsvariations and build array for
  3449.             /// usage as method signature, plus put together a nice string for docs
  3450.  
  3451.             $ret array('function' => $xmlrpcfuncname'signature' => $sigs'docstring' => $desc);
  3452.             return $ret;
  3453.         }
  3454.     }
  3455.  
  3456.     /**
  3457.     * Given an xmlrpc client and a method name, register a php wrapper function
  3458.     * that will call it and return results using native php types for both
  3459.     * params and results. The generated php function will return an xmlrpcresp
  3460.     * oject for failed xmlrpc calls
  3461.     *
  3462.     * Known limitations:
  3463.     * - server must support system.methodsignature for the wanted xmlrpc method
  3464.     * - for methods that expose many signatures, only one can be picked (we
  3465.     *   could in priciple check if signatures differ only by number of params
  3466.     *   and not by type, but it would be more complication than we can spare time)
  3467.     * - nested xmlrpc params: the caller of the generated php function has to
  3468.     *   encode on its own the params passed to the php function if these are structs
  3469.     *   or arrays whose (sub)members include values of type datetime or base64
  3470.     *
  3471.     * Notes: the connection properties of the given client will be copied
  3472.     * and reused for the connection used during the call to the generated
  3473.     * php function.
  3474.     * Calling the generated php function 'might' be slow: a new xmlrpc client
  3475.     * is created on every invocation and an xmlrpc-connection opened+closed.
  3476.     * An extra 'debug' param is appended to param list of xmlrpc method, useful
  3477.     * for debugging purposes.
  3478.     *
  3479.     * @param xmlrpc_client $client     an xmlrpc client set up correctly to communicate with target server
  3480.     * @param string        $methodname the xmlrpc method to be mapped to a php function
  3481.     * @param integer       $signum     the index of the method signature to use in mapping (if method exposes many sigs)
  3482.     * @return string                   the name of the generated php function (or false)
  3483.     */
  3484.     function wrap_xmlrpc_method($client$methodname$signum=0$timeout=0$protocol=''$newfuncname='')
  3485.     {
  3486.         $msg new xmlrpcmsg('system.methodSignature');
  3487.         $msg->addparam(new xmlrpcval($methodname));
  3488.         $response =$client->send($msg$timeout$protocol);
  3489.         if(!$response || $response->faultCode())
  3490.         {
  3491.             return false;
  3492.         }
  3493.         else
  3494.         {
  3495.             $desc $response->value();
  3496.             if(($client->return_type == 'xmlrpcvals' && ($desc->kindOf(!= 'array' || $desc->arraysize(<= $signum)) ||
  3497.                 ($client->return_type == 'phpvals' && (!is_array($desc|| count($desc<= $signum)))
  3498.             {
  3499.                 return false;
  3500.             }
  3501.             else
  3502.             {
  3503.                 if($newfuncname != '')
  3504.                 {
  3505.                     $xmlrpcfuncname $newfuncname;
  3506.                 }
  3507.                 else
  3508.                 {
  3509.                     $xmlrpcfuncname 'xmlrpc_'.str_replace('.''_'$methodname);
  3510.                 }
  3511.                 while(function_exists($xmlrpcfuncname))
  3512.                 {
  3513.                     $xmlrpcfuncname .= 'x';
  3514.                 }
  3515.                 if ($client->return_type == 'phpvals')
  3516.                 {
  3517.                     $desc $desc[$signum];
  3518.                 }
  3519.                 else
  3520.                 {
  3521.                     $desc $desc->arraymem($signum);
  3522.                 }
  3523.                 $code "function $xmlrpcfuncname (";
  3524.                 $innercode "\$client = new xmlrpc_client('$client->path', '$client->server');\n";
  3525.                 // copy all client fields to the client that will be generated runtime
  3526.                 // (this provides for future expansion of client obj)
  3527.                 foreach($client as $fld => $val)
  3528.                 {
  3529.                     if($fld != 'debug' && $fld != 'return_type')
  3530.                     {
  3531.                         $val var_export($valtrue);
  3532.                         $innercode .= "\$client->$fld = $val;\n";
  3533.                     }
  3534.                 }
  3535.                 $innercode .= "\$client->setDebug(\$debug);\n";
  3536.                 $innercode .= "\$client->return_type = 'xmlrpcvals';\n";
  3537.                 $innercode .= "\$msg = new xmlrpcmsg('$methodname');\n";
  3538.  
  3539.                 // param parsing
  3540.                 $plist array();
  3541.                 if ($client->return_type == 'phpvals')
  3542.                 {
  3543.                     $pcount count($desc);
  3544.                 }
  3545.                 else
  3546.                 {
  3547.                     $pcount $desc->arraysize();
  3548.                 }
  3549.                 for($i 1$i $pcount$i++)
  3550.                 {
  3551.                     $plist["\$p$i";
  3552.                     if ($client->return_type == 'phpvals')
  3553.                     {
  3554.                         $ptype $desc[$i];
  3555.                     }
  3556.                     else
  3557.                     {
  3558.                         $ptype $desc->arraymem($i);
  3559.                         $ptype $ptype->scalarval();
  3560.                     }
  3561.                     if($ptype == 'dateTime.iso8601' || $ptype == 'base64')
  3562.                     {
  3563.                         $innercode .= "\$p$i = new xmlrpcval(\$p$i, '$ptype');\n";
  3564.                     }
  3565.                     else
  3566.                     {
  3567.                         $innercode .= "\$p$i =& php_xmlrpc_encode(\$p$i);\n";
  3568.                     }
  3569.                     $innercode .= "\$msg->addparam(\$p$i);\n";
  3570.                 }
  3571.                 $plist['$debug = 0';
  3572.                 $plist implode(','$plist);
  3573.  
  3574.                 $innercode .= "\$res =& \$client->send(\$msg$timeout, '$protocol');\n";
  3575.                 $innercode .= "if (\$res->faultcode()) return \$res; else return php_xmlrpc_decode(\$res->value(), array('decode_php_objs'));";
  3576.  
  3577.                 $code $code $plist") {\n" $innercode "\n}\n\$allOK=1;";
  3578.                 //print_r($code);
  3579.                 $allOK 0;
  3580.                 eval($code);
  3581.                 // alternative
  3582.                 //$xmlrpcfuncname = create_function('$m', $innercode);
  3583.                 if($allOK)
  3584.                 {
  3585.                     return $xmlrpcfuncname;
  3586.                 }
  3587.                 else
  3588.                 {
  3589.                     return false;
  3590.                 }
  3591.             }
  3592.         }
  3593.     }
  3594.  
  3595.     /**
  3596.     * xml charset encoding guessing helper function.
  3597.     * Tries to determine the charset encoding of an XML chunk
  3598.     * received over HTTP.
  3599.  
  3600.     * NB: according to the spec (RFC 3023, if text/xml content-type is received over HTTP without a content-type,
  3601.  
  3602.     * we SHOULD assume it is strictly US-ASCII. But we try to be more tolerant of unconforming (legacy?) clients/servers,
  3603.  
  3604.     * which will be most probably using UTF-8 anyway...
  3605.     *
  3606.     * @param string $httpheaders the http Content-type header
  3607.     * @param string $xmlchunk xml content buffer
  3608.     * @param string $encoding_prefs comma separated list of character encodings to be used as default (when mb extension is enabled)
  3609.     *
  3610.     * @todo explore usage of mb_http_input(): does it detect http headers + post data? if so, use it instead of hand-detection!!!
  3611.     */
  3612.     function guess_encoding($httpheader=''$xmlchunk=''$encoding_prefs=null)
  3613.     {
  3614.         // discussion: see http://www.yale.edu/pclt/encoding/
  3615.         // 1 - test if encoding is specified in HTTP HEADERS
  3616.  
  3617.         //Details:
  3618.         // LWS:           (\13\10)?( |\t)+
  3619.         // token:         (any char but excluded stuff)+
  3620.         // header:        Content-type = ...; charset=value(; ...)*
  3621.         //   where value is of type token, no LWS allowed between 'charset' and value
  3622.         // Note: we do not check for invalid chars in VALUE:
  3623.         //   this had better be done using pure ereg as below
  3624.  
  3625.         /// @todo this test will pass if ANY header has charset specification, not only Content-Type. Fix it?
  3626.         if(eregi(";((\\xD\\xA)?[ \\x9]+)*charset="$httpheader))
  3627.         {
  3628.             /// @BUG if charset is received uppercase, this line will fail!
  3629.             $in strpos($httpheader'charset=')+8;
  3630.             $out strpos($httpheader';'$instrpos($httpheader';'$instrlen($httpheader);
  3631.             return strtoupper(trim(substr($httpheader$in$out-$in)));
  3632.         }
  3633.  
  3634.         // 2 - scan the first bytes of the data for a UTF-16 (or other) BOM pattern
  3635.         //     (source: http://www.w3.org/TR/2000/REC-xml-20001006)
  3636.         //     NOTE: actually, according to the spec, even if we find the BOM and determine
  3637.         //     an encoding, we should check if there is an encoding specified
  3638.         //     in the xml declaration, and verify if they match.
  3639.         /// @todo implement check as described above?
  3640.         /// @todo implement check for first bytes of string even without a BOM? (It sure looks harder than for cases WITH a BOM)
  3641.         if(@ereg("^(\\x00\\x00\\xFE\\xFF|\\xFF\\xFE\\x00\\x00|\\x00\\x00\\xFF\\xFE|\\xFE\\xFF\\x00\\x00)"$xmlchunk))
  3642.         //  if (preg_match("/^(\\x00\\x00\\xFE\\xFF|\\xFF\\xFE\\x00\\x00|\\x00\\x00\\xFF\\xFE|\\xFE\\xFF\\x00\\x00)/", $xmlchunk))
  3643.         {
  3644.             return 'UCS-4';
  3645.         }
  3646.         elseif(ereg("^(\\xFE\\xFF|\\xFF\\xFE)"$xmlchunk))
  3647.         {
  3648.             return 'UTF-16';
  3649.         }
  3650.         elseif(ereg("^(\\xEF\\xBB\\xBF)"$xmlchunk))
  3651.         {
  3652.             return 'UTF-8';
  3653.         }
  3654.  
  3655.         // 3 - test if encoding is specified in the xml declaration
  3656.         // Details:
  3657.         // SPACE:         (#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+
  3658.         // EQ:            SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]*
  3659.         if (ereg("^<\?xml".
  3660.             "[ \\x9\\xD\\xA]+" "version"  "[ \\x9\\xD\\xA]*=[ \\x9\\xD\\xA]*" "((\"[a-zA-Z0-9_.:-]+\")|('[a-zA-Z0-9_.:-]+'))".
  3661.             "[ \\x9\\xD\\xA]+" "encoding" "[ \\x9\\xD\\xA]*=[ \\x9\\xD\\xA]*" "((\"[A-Za-z][A-Za-z0-9._-]*\")|('[A-Za-z][A-Za-z0-9._-]*'))",
  3662.             $xmlchunk$regs))
  3663.         {
  3664.             return strtoupper(substr($regs[4]1strlen($regs[4])-2));
  3665.         }
  3666.  
  3667.         // 4 - if mbstring is available, let it do the guesswork
  3668.         // NB: we favour finding an encoding that is compatible with what we can process
  3669.         if(extension_loaded('mbstring'))
  3670.         {
  3671.             if($encoding_prefs)
  3672.             {
  3673.                 $enc mb_detect_encoding($xmlchunk$encoding_prefs);
  3674.             }
  3675.             else
  3676.             {
  3677.                 $enc mb_detect_encoding($xmlchunk);
  3678.             }
  3679.             // NB: mb_detect likes to call it ascii, xml parser likes to call it US_ASCII...
  3680.             // IANA also likes better US-ASCII, so go with it
  3681.             if($enc == 'ASCII')
  3682.             {
  3683.                 $enc 'US-'.$enc;
  3684.             }
  3685.             return $enc;
  3686.         }
  3687.         else
  3688.         {
  3689.             // no encoding specified: as per HTTP1.1 assume it is iso-8859-1?
  3690.             // Both RFC 2616 (HTTP 1.1) and 1945(http 1.0) clearly state that for text/xxx content types
  3691.             // this should be the standard. And we should be getting text/xml as request and response.
  3692.             // BUT we have to be backward compatible with the lib, which always used UTF-8 as default...
  3693.             return $GLOBALS['xmlrpc_defencoding'];
  3694.         }
  3695.     }
  3696.  
  3697. /**
  3698. * Checks if a given charset encoding is present in a list of encodings or
  3699. * if it is a valid subset of any encoding in the list
  3700. @param string $encoding  charset to be tested
  3701. @param mixed  $validlist comma separated list of valid charsets (or array of charsets)
  3702. */
  3703. function is_valid_charset($encoding$validlist)
  3704. {
  3705.     $charset_supersets array(
  3706.     'US-ASCII' => array ('ISO-8859-1''ISO-8859-2''ISO-8859-3''ISO-8859-4',
  3707.                          'ISO-8859-5''ISO-8859-6''ISO-8859-7''ISO-8859-8',
  3708.                          'ISO-8859-9''ISO-8859-10''ISO-8859-11''ISO-8859-12',
  3709.                          'ISO-8859-13''ISO-8859-14''ISO-8859-15''UTF-8',
  3710.                          'EUC-JP''EUC-''EUC-KR''EUC-CN')
  3711.     );
  3712.     if (is_string($validlist))
  3713.         $validlist explode(','$validlist);
  3714.     if (@in_array(strtoupper($encoding)$validlist))
  3715.         return true;
  3716.     else
  3717.     {
  3718.         if (array_key_exists($encoding$charset_supersets))
  3719.             foreach ($validlist as $allowed)
  3720.                 if (in_array($allowed$charset_supersets[$encoding]))
  3721.                     return true;
  3722.         return false;
  3723.     }
  3724. }
  3725.  
  3726. ?>

Documentation generated on Mon, 05 Mar 2007 21:32:35 +0000 by phpDocumentor 1.3.1