2 // by Edd Dumbill (C) 1999-2002
5 // Copyright (c) 1999,2000,2002 Edd Dumbill.
6 // All rights reserved.
8 // Redistribution and use in source and binary forms, with or without
9 // modification, are permitted provided that the following conditions
12 // * Redistributions of source code must retain the above copyright
13 // notice, this list of conditions and the following disclaimer.
15 // * Redistributions in binary form must reproduce the above
16 // copyright notice, this list of conditions and the following
17 // disclaimer in the documentation and/or other materials provided
18 // with the distribution.
20 // * Neither the name of the "XML-RPC for PHP" nor the names of its
21 // contributors may be used to endorse or promote products derived
22 // from this software without specific prior written permission.
24 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35 // OF THE POSSIBILITY OF SUCH DAMAGE.
37 if (!function_exists('xml_parser_create')) {
38 // Win 32 fix. From: 'Leo West' <lwest@imaginet.fr>
46 // G. Giunta 2005/01/29: declare global these variables,
47 // so that xmlrpc.inc will work even if included from within a function
48 // NB: it will give warnings in PHP3, so we comment it out
49 // Milosch: Next round, maybe we should explicitly request these via $GLOBALS where used.
50 if (phpversion() >= '4') {
54 global $xmlrpcBoolean;
56 global $xmlrpcDateTime;
65 global $xmlrpc_defencoding;
66 global $xmlrpc_internalencoding;
68 global $xmlrpcVersion;
69 global $xmlrpcerruser;
71 global $xmlrpc_backslash;
76 $xmlrpcBoolean = 'boolean';
77 $xmlrpcDouble = 'double';
78 $xmlrpcString = 'string';
79 $xmlrpcDateTime = 'dateTime.iso8601';
80 $xmlrpcBase64 = 'base64';
81 $xmlrpcArray = 'array';
82 $xmlrpcStruct = 'struct';
96 $xmlrpc_valid_parents = array(
97 'BOOLEAN' => array('VALUE'),
98 'I4' => array('VALUE'),
99 'INT' => array('VALUE'),
100 'STRING' => array('VALUE'),
101 'DOUBLE' => array('VALUE'),
102 'DATETIME.ISO8601' => array('VALUE'),
103 'BASE64' => array('VALUE'),
104 'ARRAY' => array('VALUE'),
105 'STRUCT' => array('VALUE'),
106 'PARAM' => array('PARAMS'),
107 'METHODNAME' => array('METHODCALL'),
108 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
109 'MEMBER' => array('STRUCT'),
110 'NAME' => array('MEMBER'),
111 'DATA' => array('ARRAY'),
112 'FAULT' => array('METHODRESPONSE'),
113 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
116 $xmlEntities = array(
124 $xmlrpcerr['unknown_method'] = 1;
125 $xmlrpcstr['unknown_method'] = 'Unknown method';
126 $xmlrpcerr['invalid_return'] = 2;
127 $xmlrpcstr['invalid_return'] = 'Invalid return payload: enable debugging to examine incoming payload';
128 $xmlrpcerr['incorrect_params'] = 3;
129 $xmlrpcstr['incorrect_params'] = 'Incorrect parameters passed to method';
130 $xmlrpcerr['introspect_unknown'] = 4;
131 $xmlrpcstr['introspect_unknown'] = "Can't introspect: method unknown";
132 $xmlrpcerr['http_error'] = 5;
133 $xmlrpcstr['http_error'] = "Didn't receive 200 OK from remote server.";
134 $xmlrpcerr['no_data'] = 6;
135 $xmlrpcstr['no_data'] = 'No data received from server.';
136 $xmlrpcerr['no_ssl'] = 7;
137 $xmlrpcstr['no_ssl'] = 'No SSL support compiled in.';
138 $xmlrpcerr['curl_fail'] = 8;
139 $xmlrpcstr['curl_fail'] = 'CURL error';
140 $xmlrpcerr['invalid_request'] = 15;
141 $xmlrpcstr['invalid_request'] = 'Invalid request payload';
143 $xmlrpcerr['multicall_notstruct'] = 9;
144 $xmlrpcstr['multicall_notstruct'] = 'system.multicall expected struct';
145 $xmlrpcerr['multicall_nomethod'] = 10;
146 $xmlrpcstr['multicall_nomethod'] = 'missing methodName';
147 $xmlrpcerr['multicall_notstring'] = 11;
148 $xmlrpcstr['multicall_notstring'] = 'methodName is not a string';
149 $xmlrpcerr['multicall_recursion'] = 12;
150 $xmlrpcstr['multicall_recursion'] = 'recursive system.multicall forbidden';
151 $xmlrpcerr['multicall_noparams'] = 13;
152 $xmlrpcstr['multicall_noparams'] = 'missing params';
153 $xmlrpcerr['multicall_notarray'] = 14;
154 $xmlrpcstr['multicall_notarray'] = 'params is not an array';
156 // The charset encoding expected by the server for received messages and
157 // by the client for received responses
158 $xmlrpc_defencoding = 'UTF-8';
159 // The encoding used by PHP.
160 // String values received will be converted to this.
161 $xmlrpc_internalencoding = 'ISO-8859-1';
163 $xmlrpcName = 'XML-RPC for PHP';
164 $xmlrpcVersion = '1.2';
166 // let user errors start at 800
167 $xmlrpcerruser = 800;
168 // let XML parse errors start at 100
171 // formulate backslashes for escaping regexp
172 $xmlrpc_backslash = chr(92) . chr(92);
174 // used to store state during parsing
175 // quick explanation of components:
176 // ac - used to accumulate values
177 // isf - used to indicate a fault
178 // lv - used to indicate "looking for a value": implements
179 // the logic to allow values with no types to be strings
180 // params - used to store parameters in method calls
181 // method - used to store method name
182 // stack - array with genealogy of xml elements names:
183 // used to validate nesting of xmlrpc elements
188 * To help correct communication of non-ascii chars inside strings, regardless
189 * of the charset used when sending requests, parsing them, sending responses
190 * and parsing responses, convert all non-ascii chars present in the message
191 * into their equivalent 'charset entity'. Charset entities enumerated this way
192 * are independent of the charset encoding used to transmit them, and all XML
193 * parsers are bound to understand them.
195 function xmlrpc_entity_decode($string)
197 $top = explode('&', $string);
200 while ($i < sizeof($top)) {
201 if (ereg("^([#a-zA-Z0-9]+);", $top[$i], $regs)) {
202 $op .= ereg_replace("^[#a-zA-Z0-9]+;",
203 xmlrpc_lookup_entity($regs[1]),
209 $op .= '&' . $top[$i];
217 function xmlrpc_lookup_entity($ent)
221 if (isset($xmlEntities[strtolower($ent)])) {
222 return $xmlEntities[strtolower($ent)];
224 if (ereg("^#([0-9]+)$", $ent, $regs)) {
225 return chr($regs[1]);
231 * These entities originate from HTML specs (1.1, proposed 2.0, etc),
232 * and are taken directly from php-4.3.1/ext/mbstring/html_entities.c.
233 * Until php provides functionality to translate these entities in its
234 * core library, use this function.
236 function xmlrpc_html_entity_xlate($data = '')
239 " " => " ",
240 "¡" => "¡",
241 "¢" => "¢",
242 "£" => "£",
243 "¤" => "¤",
245 "¦" => "¦",
246 "§" => "§",
248 "©" => "©",
249 "ª" => "ª",
250 "«" => "«",
254 "¯" => "¯",
256 "±" => "±",
257 "²" => "²",
258 "³" => "³",
259 "´" => "´",
260 "µ" => "µ",
261 "¶" => "¶",
262 "·" => "·",
263 "¸" => "¸",
264 "¹" => "¹",
265 "º" => "º",
266 "»" => "»",
267 "¼" => "¼",
268 "½" => "½",
269 "¾" => "¾",
270 "¿" => "¿",
271 "À" => "À",
272 "Á" => "Á",
273 "Â" => "Â",
274 "Ã" => "Ã",
275 "Ä" => "Ä",
276 "Å" => "Å",
277 "Æ" => "Æ",
278 "Ç" => "Ç",
279 "È" => "È",
280 "É" => "É",
281 "Ê" => "Ê",
282 "Ë" => "Ë",
283 "Ì" => "Ì",
284 "Í" => "Í",
285 "Î" => "Î",
286 "Ï" => "Ï",
288 "Ñ" => "Ñ",
289 "Ò" => "Ò",
290 "Ó" => "Ó",
291 "Ô" => "Ô",
292 "Õ" => "Õ",
293 "Ö" => "Ö",
294 "×" => "×",
295 "Ø" => "Ø",
296 "Ù" => "Ù",
297 "Ú" => "Ú",
298 "Û" => "Û",
299 "Ü" => "Ü",
300 "Ý" => "Ý",
301 "Þ" => "Þ",
302 "ß" => "ß",
303 "à" => "à",
304 "á" => "á",
305 "â" => "â",
306 "ã" => "ã",
307 "ä" => "ä",
308 "å" => "å",
309 "æ" => "æ",
310 "ç" => "ç",
311 "è" => "è",
312 "é" => "é",
313 "ê" => "ê",
314 "ë" => "ë",
315 "ì" => "ì",
316 "í" => "í",
317 "î" => "î",
318 "ï" => "ï",
320 "ñ" => "ñ",
321 "ò" => "ò",
322 "ó" => "ó",
323 "ô" => "ô",
324 "õ" => "õ",
325 "ö" => "ö",
326 "÷" => "÷",
327 "ø" => "ø",
328 "ù" => "ù",
329 "ú" => "ú",
330 "û" => "û",
331 "ü" => "ü",
332 "ý" => "ý",
333 "þ" => "þ",
334 "ÿ" => "ÿ",
335 "Œ" => "Œ",
336 "œ" => "œ",
337 "Š" => "Š",
338 "š" => "š",
339 "Ÿ" => "Ÿ",
340 "ƒ" => "ƒ",
341 "ˆ" => "ˆ",
342 "˜" => "˜",
343 "Α" => "Α",
344 "Β" => "Β",
345 "Γ" => "Γ",
346 "Δ" => "Δ",
347 "Ε" => "Ε",
348 "Ζ" => "Ζ",
350 "Θ" => "Θ",
351 "Ι" => "Ι",
352 "Κ" => "Κ",
353 "Λ" => "Λ",
357 "Ο" => "Ο",
360 "Σ" => "Σ",
362 "Υ" => "Υ",
366 "Ω" => "Ω",
367 "β" => "β",
368 "γ" => "γ",
369 "δ" => "δ",
370 "ε" => "ε",
371 "ζ" => "ζ",
373 "θ" => "θ",
374 "ι" => "ι",
375 "κ" => "κ",
376 "λ" => "λ",
380 "ο" => "ο",
383 "ς" => "ς",
384 "σ" => "σ",
386 "υ" => "υ",
390 "ω" => "ω",
391 "ϑ" => "ϑ",
392 "ϒ" => "ϒ",
394 " " => " ",
395 " " => " ",
396 " " => " ",
397 "‌" => "‌",
398 "‍" => "‍",
399 "‎" => "‎",
400 "‏" => "‏",
401 "–" => "–",
402 "—" => "—",
403 "‘" => "‘",
404 "’" => "’",
405 "‚" => "‚",
406 "“" => "“",
407 "”" => "”",
408 "„" => "„",
409 "†" => "†",
410 "‡" => "‡",
411 "•" => "•",
412 "…" => "…",
413 "‰" => "‰",
414 "′" => "′",
415 "″" => "″",
416 "‹" => "‹",
417 "›" => "›",
418 "‾" => "‾",
419 "⁄" => "⁄",
420 "€" => "€",
421 "℘" => "℘",
422 "ℑ" => "ℑ",
423 "ℜ" => "ℜ",
424 "™" => "™",
425 "ℵ" => "ℵ",
426 "←" => "←",
427 "↑" => "↑",
428 "→" => "→",
429 "↓" => "↓",
430 "↔" => "↔",
431 "↵" => "↵",
432 "⇐" => "⇐",
433 "⇑" => "⇑",
434 "⇒" => "⇒",
435 "⇓" => "⇓",
436 "⇔" => "⇔",
437 "∀" => "∀",
438 "∂" => "∂",
439 "∃" => "∃",
440 "∅" => "∅",
441 "∇" => "∇",
442 "∈" => "∈",
443 "∉" => "∉",
445 "∏" => "∏",
446 "∑" => "∑",
447 "−" => "−",
448 "∗" => "∗",
449 "√" => "√",
450 "∝" => "∝",
451 "∞" => "∞",
452 "∠" => "∠",
453 "∧" => "∧",
455 "∩" => "∩",
456 "∪" => "∪",
457 "∫" => "∫",
458 "∴" => "∴",
459 "∼" => "∼",
460 "≅" => "≅",
461 "≈" => "≈",
463 "≡" => "≡",
466 "⊂" => "⊂",
467 "⊃" => "⊃",
468 "⊄" => "⊄",
469 "⊆" => "⊆",
470 "⊇" => "⊇",
471 "⊕" => "⊕",
472 "⊗" => "⊗",
473 "⊥" => "⊥",
474 "⋅" => "⋅",
475 "⌈" => "⌈",
476 "⌉" => "⌉",
477 "⌊" => "⌊",
478 "⌋" => "⌋",
479 "⟨" => "〈",
480 "⟩" => "〉",
481 "◊" => "◊",
482 "♠" => "♠",
483 "♣" => "♣",
484 "♥" => "♥",
485 "♦" => "♦");
486 return strtr($data, $entities);
489 function xmlrpc_encode_entitites($data)
491 $length = strlen($data);
493 for ($position = 0; $position < $length; $position++) {
494 $character = substr($data, $position, 1);
495 $code = Ord($character);
498 $character = """;
501 $character = "&";
504 $character = "'";
513 if ($code < 32 || $code > 159)
514 $character = ("&#" . strval($code) . ";");
517 $escapeddata .= $character;
522 function xmlrpc_se($parser, $name, $attrs)
524 global $_xh, $xmlrpcDateTime, $xmlrpcString, $xmlrpc_valid_parents;
526 // if invalid xmlrpc already detected, skip all processing
527 if ($_xh[$parser]['isf'] < 2) {
529 // check for correct element nesting
530 // top level element can only be of 2 types
531 if (count($_xh[$parser]['stack']) == 0) {
532 if ($name != 'METHODRESPONSE' && $name != 'METHODCALL') {
533 $_xh[$parser]['isf'] = 2;
534 $_xh[$parser]['isf_reason'] = 'missing top level xmlrpc element';
538 // not top level element: see if parent is OK
539 if (!in_array($_xh[$parser]['stack'][0], $xmlrpc_valid_parents[$name])) {
540 $_xh[$parser]['isf'] = 2;
541 $_xh[$parser]['isf_reason'] = "xmlrpc element $name cannot be child of {$_xh[$parser]['stack'][0]}";
549 //$_xh[$parser]['st'].='array(';
550 //$_xh[$parser]['cm']++;
551 // this last line turns quoting off
552 // this means if we get an empty array we'll
553 // simply get a bit of whitespace in the eval
554 //$_xh[$parser]['qt']=0;
556 // create an empty array to hold child values, and push it onto appropriate stack
558 $cur_val['values'] = array();
559 $cur_val['type'] = $name;
560 array_unshift($_xh[$parser]['valuestack'], $cur_val);
564 //$_xh[$parser]['st'].='"';
565 $_xh[$parser]['ac'] = '';
568 $_xh[$parser]['isf'] = 1;
571 //$_xh[$parser]['st']='';
572 // clear value, so we can check later if no value will passed for this param/member
573 $_xh[$parser]['value'] = null;
576 //$_xh[$parser]['st'].='new xmlrpcval(';
577 // look for a value: if this is still true by the
578 // time we reach the end tag for value then the type is string
580 $_xh[$parser]['vt'] = 'value';
581 $_xh[$parser]['ac'] = '';
582 //$_xh[$parser]['qt']=0;
583 $_xh[$parser]['lv'] = 1;
590 case 'DATETIME.ISO8601':
592 if ($_xh[$parser]['vt'] != 'value') {
593 //two data elements inside a value: an error occurred!
594 $_xh[$parser]['isf'] = 2;
595 $_xh[$parser]['isf_reason'] = "$name element following a {$_xh[$parser]['vt']} element inside a single value";
599 // reset the accumulator
600 $_xh[$parser]['ac'] = '';
602 /*if ($name=='DATETIME.ISO8601' || $name=='STRING')
604 $_xh[$parser]['qt']=1;
605 if ($name=='DATETIME.ISO8601')
607 $_xh[$parser]['vt']=$xmlrpcDateTime;
610 elseif ($name=='BASE64')
612 $_xh[$parser]['qt']=2;
616 // No quoting is required here -- but
617 // at the end of the element we must check
618 // for data format errors.
619 $_xh[$parser]['qt']=0;
623 //$_xh[$parser]['ac']='';
624 // avoid warnings later on if no NAME is found before VALUE inside
625 // a struct member predefining member name as NULL
626 $_xh[$parser]['valuestack'][0]['name'] = '';
627 // clear value, so we can check later if no value will passed for this param/member
628 $_xh[$parser]['value'] = null;
632 case 'METHODRESPONSE':
634 // valid elements that add little to processing
637 /// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
638 $_xh[$parser]['isf'] = 2;
639 $_xh[$parser]['isf_reason'] = "found not-xmlrpc xml element $name";
643 // Save current element name to stack, to validate nesting
644 array_unshift($_xh[$parser]['stack'], $name);
646 if ($name != 'VALUE') {
647 $_xh[$parser]['lv'] = 0;
652 function xmlrpc_ee($parser, $name)
654 global $_xh, $xmlrpcTypes, $xmlrpcString, $xmlrpcDateTime;
656 if ($_xh[$parser]['isf'] < 2) {
658 // push this element name from stack
659 // NB: if XML validates, correct opening/closing is guaranteed and
660 // we do not have to check for $name == $curr_elem.
661 // we also checked for proper nesting at start of elements...
662 $curr_elem = array_shift($_xh[$parser]['stack']);
667 //if ($_xh[$parser]['cm'] && substr($_xh[$parser]['st'], -1) ==',')
669 // $_xh[$parser]['st']=substr($_xh[$parser]['st'],0,-1);
671 //$_xh[$parser]['st'].=')';
673 // fetch out of stack array of values, and promote it to current value
674 $cur_val = array_shift($_xh[$parser]['valuestack']);
675 $_xh[$parser]['value'] = $cur_val['values'];
677 $_xh[$parser]['vt'] = strtolower($name);
678 //$_xh[$parser]['cm']--;
681 //$_xh[$parser]['st'].= $_xh[$parser]['ac'] . '" => ';
682 $_xh[$parser]['valuestack'][0]['name'] = $_xh[$parser]['ac'];
689 case 'DATETIME.ISO8601':
691 $_xh[$parser]['vt'] = strtolower($name);
692 //if ($_xh[$parser]['qt']==1)
693 if ($name == 'STRING') {
694 // we use double quotes rather than single so backslashification works OK
695 //$_xh[$parser]['st'].='"'. $_xh[$parser]['ac'] . '"';
696 $_xh[$parser]['value'] = $_xh[$parser]['ac'];
697 } elseif ($name == 'DATETIME.ISO8601') {
698 $_xh[$parser]['vt'] = $xmlrpcDateTime;
699 $_xh[$parser]['value'] = $_xh[$parser]['ac'];
700 } elseif ($name == 'BASE64') {
701 //$_xh[$parser]['st'].='base64_decode("'. $_xh[$parser]['ac'] . '")';
703 ///@todo check for failure of base64 decoding / catch warnings
704 $_xh[$parser]['value'] = base64_decode($_xh[$parser]['ac']);
705 } elseif ($name == 'BOOLEAN') {
706 // special case here: we translate boolean 1 or 0 into PHP
707 // constants true or false
708 // NB: this simple checks helps a lot sanitizing input, ie no
709 // security problems around here
710 if ($_xh[$parser]['ac'] == '1') {
711 //$_xh[$parser]['ac']='true';
712 $_xh[$parser]['value'] = true;
714 //$_xh[$parser]['ac']='false';
715 // log if receiveing something strange, even though we set the value to false anyway
716 if ($_xh[$parser]['ac'] != '0')
717 error_log('XML-RPC: invalid value received in BOOLEAN: ' . $_xh[$parser]['ac']);
718 $_xh[$parser]['value'] = false;
720 //$_xh[$parser]['st'].=$_xh[$parser]['ac'];
721 } elseif ($name == 'DOUBLE') {
723 // we must check that only 0123456789-.<space> are characters here
724 if (!ereg("^[+-]?[eE0123456789 \\t\\.]+$", $_xh[$parser]['ac'])) {
725 // TODO: find a better way of throwing an error
727 error_log('XML-RPC: non numeric value received in DOUBLE: ' . $_xh[$parser]['ac']);
728 //$_xh[$parser]['st'].="'ERROR_NON_NUMERIC_FOUND'";
729 $_xh[$parser]['value'] = 'ERROR_NON_NUMERIC_FOUND';
731 // it's ok, add it on
732 //$_xh[$parser]['st'].=(double)$_xh[$parser]['ac'];
733 $_xh[$parser]['value'] = (double)$_xh[$parser]['ac'];
737 // we must check that only 0123456789-<space> are characters here
738 if (!ereg("^[+-]?[0123456789 \\t]+$", $_xh[$parser]['ac'])) {
739 // TODO: find a better way of throwing an error
741 error_log('XML-RPC: non numeric value received in INT: ' . $_xh[$parser]['ac']);
742 //$_xh[$parser]['st'].="'ERROR_NON_NUMERIC_FOUND'";
743 $_xh[$parser]['value'] = 'ERROR_NON_NUMERIC_FOUND';
745 // it's ok, add it on
746 //$_xh[$parser]['st'].=(int)$_xh[$parser]['ac'];
747 $_xh[$parser]['value'] = (int)$_xh[$parser]['ac'];
750 $_xh[$parser]['ac'] = '';
751 //$_xh[$parser]['qt']=0;
752 $_xh[$parser]['lv'] = 3; // indicate we've found a value
755 // This if() detects if no scalar was inside <VALUE></VALUE>
756 if ($_xh[$parser]['vt'] == 'value') {
757 $_xh[$parser]['value'] = $_xh[$parser]['ac'];
758 $_xh[$parser]['vt'] = $xmlrpcString;
760 /*if (strlen($_xh[$parser]['ac'])>0 &&
761 $_xh[$parser]['vt']==$xmlrpcString)
763 $_xh[$parser]['st'].='"'. $_xh[$parser]['ac'] . '"';
765 // This if() detects if no scalar was inside <VALUE></VALUE>
766 // and pads an empty ''.
767 if($_xh[$parser]['st'][strlen($_xh[$parser]['st'])-1] == '(')
769 $_xh[$parser]['st'].= '""';
771 // G. Giunta 2005/03/12 save some chars in the reconstruction of string vals...
772 if ($_xh[$parser]['vt'] != $xmlrpcString)
773 $_xh[$parser]['st'].=", '" . $_xh[$parser]['vt'] . "')";
775 $_xh[$parser]['st'].=")";
776 if ($_xh[$parser]['cm'])
778 $_xh[$parser]['st'].=',';
781 // build the xmlrpc val out of the data received, and substitute it
782 $temp = new xmlrpcval($_xh[$parser]['value'], $_xh[$parser]['vt']);
783 // check if we are inside an array or struct:
784 // if value just built is inside an array, let's move it into array on the stack
785 if (count($_xh[$parser]['valuestack']) && $_xh[$parser]['valuestack'][0]['type'] == 'ARRAY') {
786 $_xh[$parser]['valuestack'][0]['values'][] = $temp;
788 $_xh[$parser]['value'] = $temp;
792 $_xh[$parser]['ac'] = '';
793 //$_xh[$parser]['qt']=0;
794 // add to array in the stack the last element built
795 // unless no VALUE was found
796 if ($_xh[$parser]['value'])
797 $_xh[$parser]['valuestack'][0]['values'][$_xh[$parser]['valuestack'][0]['name']] = $_xh[$parser]['value'];
799 error_log('XML-RPC: missing VALUE inside STRUCT in received xml');
802 $_xh[$parser]['ac'] = '';
803 //$_xh[$parser]['qt']=0;
806 //$_xh[$parser]['params'][]=$_xh[$parser]['st'];
807 if ($_xh[$parser]['value'])
808 $_xh[$parser]['params'][] = $_xh[$parser]['value'];
810 error_log('XML-RPC: missing VALUE inside PARAM in received xml');
813 $_xh[$parser]['method'] = ereg_replace("^[\n\r\t ]+", '', $_xh[$parser]['ac']);
818 case 'METHORESPONSE':
821 // End of INVALID ELEMENT!
822 // shall we add an assert here for unreachable code???
826 // if it's a valid type name, set the type
827 /*if (isset($xmlrpcTypes[strtolower($name)]))
829 $_xh[$parser]['vt']=strtolower($name);
836 function xmlrpc_cd($parser, $data)
838 global $_xh, $xmlrpc_backslash;
840 //if (ereg("^[\n\r \t]+$", $data)) return;
841 // print "adding [${data}]\n";
843 // skip processing if xml fault already detected
844 if ($_xh[$parser]['isf'] < 2) {
845 if ($_xh[$parser]['lv'] != 3) {
846 // "lookforvalue==3" means that we've found an entire value
847 // and should discard any further character data
848 if ($_xh[$parser]['lv'] == 1) {
849 // if we've found text and we're just in a <value> then
850 // turn quoting on, as this will be a string
851 //$_xh[$parser]['qt']=1;
852 // and say we've found a value
853 $_xh[$parser]['lv'] = 2;
855 if (!@isset($_xh[$parser]['ac'])) {
856 $_xh[$parser]['ac'] = '';
858 //$_xh[$parser]['ac'].=str_replace('$', '\$', str_replace('"', '\"', str_replace(chr(92),$xmlrpc_backslash, $data)));
859 $_xh[$parser]['ac'] .= $data;
864 function xmlrpc_dh($parser, $data)
866 global $_xh, $xmlrpc_backslash;
868 // skip processing if xml fault already detected
869 if ($parser[$_xh]['isf'] < 2) {
870 if (substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';') {
871 if ($_xh[$parser]['lv'] == 1) {
872 //$_xh[$parser]['qt']=1;
873 $_xh[$parser]['lv'] = 2;
875 //$_xh[$parser]['ac'].=str_replace('$', '\$', str_replace('"', '\"', str_replace(chr(92),$xmlrpc_backslash, $data)));
876 $_xh[$parser]['ac'] .= $data;
895 var $no_multicall = false;
897 function xmlrpc_client($path, $server, $port = 0)
900 $this->server = $server;
904 function setDebug($in)
913 function setCredentials($u, $p)
915 $this->username = $u;
916 $this->password = $p;
919 function setCertificate($cert, $certpass)
922 $this->certpass = $certpass;
925 function setSSLVerifyPeer($i)
927 $this->verifypeer = $i;
930 function setSSLVerifyHost($i)
932 $this->verifyhost = $i;
935 function send($msg, $timeout = 0, $method = 'http')
937 if (is_array($msg)) {
938 // $msg is an array of xmlrpcmsg's
939 return $this->multicall($msg, $timeout, $method);
942 // where msg is an xmlrpcmsg
943 $msg->debug = $this->debug;
945 if ($method == 'https') {
946 return $this->sendPayloadHTTPS($msg,
948 $this->port, $timeout,
949 $this->username, $this->password,
953 return $this->sendPayloadHTTP10($msg, $this->server, $this->port,
954 $timeout, $this->username,
959 function sendPayloadHTTP10($msg, $server, $port, $timeout = 0, $username = '', $password = '')
961 global $xmlrpcerr, $xmlrpcstr, $xmlrpcName, $xmlrpcVersion, $xmlrpc_defencoding;
966 $fp = @fsockopen($server, $port, $this->errno, $this->errstr, $timeout);
968 $fp = @fsockopen($server, $port, $this->errno, $this->errstr);
971 if ($timeout > 0 && function_exists('stream_set_timeout'))
972 stream_set_timeout($fp, $timeout);
974 $this->errstr = 'Connect error';
975 $r = new xmlrpcresp(0, $xmlrpcerr['http_error'], $xmlrpcstr['http_error']);
978 // Only create the payload if it was not created previously
979 if (empty($msg->payload)) {
980 $msg->createPayload();
983 // thanks to Grant Rauscher <grant7@firstworld.net>
986 if ($username != '') {
987 $credentials = 'Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
990 $op = "POST " . $this->path . " HTTP/1.0\r\n" .
991 "User-Agent: " . $xmlrpcName . " " . $xmlrpcVersion . "\r\n" .
992 "Host: " . $server . "\r\n" .
994 "Accept-Charset: " . $xmlrpc_defencoding . "\r\n" .
995 "Content-Type: text/xml\r\nContent-Length: " .
996 strlen($msg->payload) . "\r\n\r\n" .
999 if (!fputs($fp, $op, strlen($op))) {
1000 $this->errstr = 'Write error';
1001 $r = new xmlrpcresp(0, $xmlrpcerr['http_error'], $xmlrpcstr['http_error']);
1004 $resp = $msg->parseResponseFile($fp);
1009 // contributed by Justin Miller <justin@voxel.net>
1010 // requires curl to be built into PHP
1011 function sendPayloadHTTPS($msg, $server, $port, $timeout = 0, $username = '', $password = '', $cert = '', $certpass = '')
1013 global $xmlrpcerr, $xmlrpcstr, $xmlrpcVersion, $xmlrpc_internalencoding;
1018 // Only create the payload if it was not created previously
1019 if (empty($msg->payload)) {
1020 $msg->createPayload();
1023 if (!function_exists('curl_init')) {
1024 $this->errstr = 'SSL unavailable on this install';
1025 $r = new xmlrpcresp(0, $xmlrpcerr['no_ssl'], $xmlrpcstr['no_ssl']);
1029 $curl = curl_init('https://' . $server . ':' . $port . $this->path);
1031 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1032 // results into variable
1034 curl_setopt($curl, CURLOPT_VERBOSE, 1);
1036 curl_setopt($curl, CURLOPT_USERAGENT, 'PHP XMLRPC ' . $xmlrpcVersion);
1037 // required for XMLRPC
1038 curl_setopt($curl, CURLOPT_POST, 1);
1040 curl_setopt($curl, CURLOPT_POSTFIELDS, $msg->payload);
1042 curl_setopt($curl, CURLOPT_HEADER, 1);
1043 // return the header too
1044 curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: text/xml', 'Accept-Charset: ' . $xmlrpc_internalencoding));
1045 // whether to verify remote host's cert
1046 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
1047 // 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
1048 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
1049 // required for XMLRPC
1051 curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
1053 // timeout is borked
1054 if ($username && $password) {
1055 curl_setopt($curl, CURLOPT_USERPWD, "$username:$password");
1059 curl_setopt($curl, CURLOPT_SSLCERT, $cert);
1063 curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass);
1065 // set cert password
1067 $result = curl_exec($curl);
1070 $this->errstr = 'no response';
1071 $resp = new xmlrpcresp(0, $xmlrpcerr['curl_fail'], $xmlrpcstr['curl_fail'] . ': ' . curl_error($curl));
1075 $resp = $msg->parseResponse($result);
1080 function multicall($msgs, $timeout = 0, $method = 'http')
1084 if (!$this->no_multicall) {
1085 $results = $this->_try_multicall($msgs, $timeout, $method);
1086 /* TODO - this is not php3-friendly */
1087 // if($results !== false)
1088 if (is_array($results)) {
1089 // Either the system.multicall succeeded, or the send
1090 // failed (e.g. due to HTTP timeout). In either case,
1091 // we're done for now.
1094 // system.multicall unsupported by server,
1095 // don't try it next time...
1096 $this->no_multicall = true;
1100 // system.multicall is unupported by server:
1101 // Emulate multicall via multiple requests
1103 //foreach($msgs as $msg)
1105 while (list(, $msg) = @each($msgs)) {
1106 $results[] = $this->send($msg, $timeout, $method);
1111 // Attempt to boxcar $msgs via system.multicall.
1112 function _try_multicall($msgs, $timeout, $method)
1114 // Construct multicall message
1116 //foreach($msgs as $msg)
1118 while (list(, $msg) = @each($msgs)) {
1119 $call['methodName'] = new xmlrpcval($msg->method(), 'string');
1120 $numParams = $msg->getNumParams();
1122 for ($i = 0; $i < $numParams; $i++) {
1123 $params[$i] = $msg->getParam($i);
1125 $call['params'] = new xmlrpcval($params, 'array');
1126 $calls[] = new xmlrpcval($call, 'struct');
1128 $multicall = new xmlrpcmsg('system.multicall');
1129 $multicall->addParam(new xmlrpcval($calls, 'array'));
1132 $result = $this->send($multicall, $timeout, $method);
1133 if (!is_object($result)) {
1134 return ($result || 0); // transport failed
1137 if ($result->faultCode() != 0) {
1138 return false; // system.multicall failed
1141 // Unpack responses.
1142 $rets = $result->value();
1143 if ($rets->kindOf() != 'array') {
1144 return false; // bad return type from system.multicall
1146 $numRets = $rets->arraysize();
1147 if ($numRets != count($msgs)) {
1148 return false; // wrong number of return values.
1151 $response = array();
1152 for ($i = 0; $i < $numRets; $i++) {
1153 $val = $rets->arraymem($i);
1154 switch ($val->kindOf()) {
1156 if ($val->arraysize() != 1) {
1157 return false; // Bad value
1159 // Normal return value
1160 $response[$i] = new xmlrpcresp($val->arraymem(0));
1163 $code = $val->structmem('faultCode');
1164 if ($code->kindOf() != 'scalar' || $code->scalartyp() != 'int') {
1167 $str = $val->structmem('faultString');
1168 if ($str->kindOf() != 'scalar' || $str->scalartyp() != 'string') {
1171 $response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
1179 } // end class xmlrpc_client
1186 var $hdrs = array();
1188 function xmlrpcresp($val, $fcode = 0, $fstr = '')
1192 $this->errno = $fcode;
1193 $this->errstr = $fstr;
1194 //$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
1195 } elseif (!is_object($val)) {
1197 error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to xmlrpcresp. Defaulting to empty value.");
1198 $this->val = new xmlrpcval();
1205 function faultCode()
1207 return $this->errno;
1210 function faultString()
1212 return $this->errstr;
1220 function serialize()
1222 $result = "<methodResponse>\n";
1224 // G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
1229 <name>faultCode</name>
1230 <value><int>' . $this->errno . '</int></value>
1233 <name>faultString</name>
1234 <value><string>' . xmlrpc_encode_entitites($this->errstr) . '</string></value>
1240 $result .= "<params>\n<param>\n" .
1241 $this->val->serialize() .
1242 "</param>\n</params>";
1244 $result .= "\n</methodResponse>";
1253 var $params = array();
1256 function xmlrpcmsg($meth, $pars = 0)
1258 $this->methodname = $meth;
1259 if (is_array($pars) && sizeof($pars) > 0) {
1260 for ($i = 0; $i < sizeof($pars); $i++) {
1261 $this->addParam($pars[$i]);
1266 function xml_header()
1268 return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
1271 function xml_footer()
1273 return "</methodCall>\n";
1276 function createPayload()
1278 $this->payload = $this->xml_header();
1279 $this->payload .= '<methodName>' . $this->methodname . "</methodName>\n";
1280 // if (sizeof($this->params)) {
1281 $this->payload .= "<params>\n";
1282 for ($i = 0; $i < sizeof($this->params); $i++) {
1283 $p = $this->params[$i];
1284 $this->payload .= "<param>\n" . $p->serialize() .
1287 $this->payload .= "</params>\n";
1289 $this->payload .= $this->xml_footer();
1290 //$this->payload=str_replace("\n", "\r\n", $this->payload);
1293 function method($meth = '')
1296 $this->methodname = $meth;
1298 return $this->methodname;
1301 function serialize()
1303 $this->createPayload();
1304 return $this->payload;
1307 function addParam($par)
1309 $this->params[] = $par;
1312 function getParam($i)
1314 return $this->params[$i];
1317 function getNumParams()
1319 return sizeof($this->params);
1322 function parseResponseFile($fp)
1325 while ($data = fread($fp, 32768)) {
1328 return $this->parseResponse($ipd);
1331 function parseResponse($data = '')
1333 global $_xh, $xmlrpcerr, $xmlrpcstr;
1334 global $xmlrpc_defencoding, $xmlrpc_internalencoding;
1338 //by maHo, replaced htmlspecialchars with htmlentities
1339 print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
1343 error_log('No response received from server.');
1344 $r = new xmlrpcresp(0, $xmlrpcerr['no_data'], $xmlrpcstr['no_data']);
1347 // see if we got an HTTP 200 OK, else bomb
1348 // but only do this if we're using the HTTP protocol.
1349 if (ereg("^HTTP", $data)) {
1350 // Strip HTTP 1.1 100 Continue header if present
1351 while (ereg('^HTTP/1.1 1[0-9]{2}', $data)) {
1352 $pos = strpos($data, 'HTTP', 12);
1353 // server sent a Continue header without any (valid) content following...
1354 // give the client a chance to know it
1355 if (!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
1357 $data = substr($data, $pos);
1359 if (!ereg("^HTTP/[0-9\\.]+ 200 ", $data)) {
1360 $errstr = substr($data, 0, strpos($data, "\n") - 1);
1361 error_log('HTTP error, got response: ' . $errstr);
1362 $r = new xmlrpcresp(0, $xmlrpcerr['http_error'], $xmlrpcstr['http_error'] . ' (' . $errstr . ')');
1366 $parser = xml_parser_create($xmlrpc_defencoding);
1368 // G. Giunta 2004/04/06
1369 // Clean up the accumulator, or it will grow indefinitely long
1370 // if making xmlrpc calls for a while
1372 $_xh[$parser] = array();
1373 $_xh[$parser]['headers'] = array();
1374 $_xh[$parser]['stack'] = array();
1375 $_xh[$parser]['valuestack'] = array();
1377 // separate HTTP headers from data
1378 if (ereg("^HTTP", $data)) {
1379 // be tolerant to usage of \n instead of \r\n to separate headers and data
1380 // (even though it is not valid http)
1381 $pos = strpos($data, "\r\n\r\n");
1382 if ($pos || is_int($pos))
1385 $pos = strpos($data, "\n\n");
1386 if ($pos || is_int($pos))
1389 // No separation between response headers and body: fault?
1393 // be tolerant to line endings, and extra empty lines
1394 $ar = explode("\r?\n", trim(substr($data, 0, $pos)));
1395 while (list(, $line) = @each($ar)) {
1396 // take care of multi-line headers
1397 $arr = explode(':', $line);
1398 if (count($arr) > 1) {
1399 $header_name = trim($arr[0]);
1400 // TO DO: some headers (the ones that allow a CSV list of values)
1401 // do allow many values to be passed using multiple header lines.
1402 // We should add content to $_xh[$parser]['headers'][$header_name]
1403 // instead of replacing it for those...
1404 $_xh[$parser]['headers'][$header_name] = $arr[1];
1405 for ($i = 2; $i < count($arr); $i++) {
1406 $_xh[$parser]['headers'][$header_name] .= ':' . $arr[$i];
1408 $_xh[$parser]['headers'][$header_name] = trim($_xh[$parser]['headers'][$header_name]);
1409 } else if (isset($header_name)) {
1410 $_xh[$parser]['headers'][$header_name] .= ' ' . trim($line);
1413 $data = substr($data, $bd);
1415 if ($this->debug && count($_xh[$parser]['headers'])) {
1417 //foreach ($_xh[$parser]['headers'] as $header)
1418 @reset($_xh[$parser]['headers']);
1419 while (list($header, $value) = @each($_xh[$parser]['headers'])) {
1420 print "HEADER: $header: $value\n";
1426 // be tolerant of extra whitespace in response body
1427 $data = trim($data);
1429 // be tolerant of junk after methodResponse (e.g. javascript automatically inserted by free hosts)
1430 // idea from Luca Mariano <luca.mariano@email.it> originally in PEARified version of the lib
1432 $pos = strpos($data, "</methodResponse>");
1433 while ($pos || is_int($pos)) {
1435 $pos = strpos($data, "</methodResponse>", $bd);
1438 $data = substr($data, 0, $bd);
1440 //$_xh[$parser]['st']='';
1441 //$_xh[$parser]['cm']=0;
1442 $_xh[$parser]['isf'] = 0;
1443 $_xh[$parser]['isf_reason'] = 0;
1444 $_xh[$parser]['ac'] = '';
1445 //$_xh[$parser]['qt']='';
1447 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
1448 // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
1449 // the xml parser to give us back data in the expected charset
1450 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $xmlrpc_internalencoding);
1452 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
1453 xml_set_character_data_handler($parser, 'xmlrpc_cd');
1454 xml_set_default_handler($parser, 'xmlrpc_dh');
1455 //$xmlrpc_value=new xmlrpcval;
1457 if (!xml_parse($parser, $data, sizeof($data))) {
1458 // thanks to Peter Kocks <peter.kocks@baygate.com>
1459 if ((xml_get_current_line_number($parser)) == 1) {
1460 $errstr = 'XML error at line 1, check URL';
1462 $errstr = sprintf('XML error: %s at line %d',
1463 xml_error_string(xml_get_error_code($parser)),
1464 xml_get_current_line_number($parser));
1467 $r = new xmlrpcresp(0, $xmlrpcerr['invalid_return'], $xmlrpcstr['invalid_return'] . ' (' . $errstr . ')');
1468 xml_parser_free($parser);
1471 $r->hdrs = $_xh[$parser]['headers'];
1474 xml_parser_free($parser);
1476 if ($_xh[$parser]['isf'] > 1) {
1478 ///@todo echo something for user?
1481 $r = new xmlrpcresp(0, $xmlrpcerr['invalid_return'],
1482 $xmlrpcstr['invalid_return'] . ' ' . $_xh[$parser]['isf_reason']);
1483 } //else if (strlen($_xh[$parser]['st'])==0)
1484 else if (!is_object($_xh[$parser]['value'])) {
1485 // then something odd has happened
1486 // and it's time to generate a client side error
1487 // indicating something odd went on
1488 $r = new xmlrpcresp(0, $xmlrpcerr['invalid_return'],
1489 $xmlrpcstr['invalid_return']);
1493 //print "<PRE>---EVALING---[" .
1494 //strlen($_xh[$parser]['st']) . " chars]---\n" .
1495 //htmlspecialchars($_xh[$parser]['st']) . ";\n---END---</PRE>";
1496 print "<PRE>---PARSED---\n";
1497 var_dump($_xh[$parser]['value']);
1498 print "\n---END---</PRE>";
1502 //@eval('$v=' . $_xh[$parser]['st'] . '; $allOK=1;');
1505 // $r = new xmlrpcresp(0, $xmlrpcerr['invalid_return'], $xmlrpcstr['invalid_return']);
1508 $v = $_xh[$parser]['value'];
1509 if ($_xh[$parser]['isf']) {
1510 $errno_v = $v->structmem('faultCode');
1511 $errstr_v = $v->structmem('faultString');
1512 $errno = $errno_v->scalarval();
1515 // FAULT returned, errno needs to reflect that
1519 $r = new xmlrpcresp($v, $errno, $errstr_v->scalarval());
1521 $r = new xmlrpcresp($v);
1525 $r->hdrs = $_xh[$parser]['headers'];
1535 function xmlrpcval($val = -1, $type = '')
1537 global $xmlrpcTypes;
1538 $this->me = array();
1540 if ($val != -1 || !is_int($val) || $type != '') {
1544 if ($xmlrpcTypes[$type] == 1) {
1545 $this->addScalar($val, $type);
1546 } elseif ($xmlrpcTypes[$type] == 2) {
1547 $this->addArray($val);
1548 } elseif ($xmlrpcTypes[$type] == 3) {
1549 $this->addStruct($val);
1554 function addScalar($val, $type = 'string')
1556 global $xmlrpcTypes, $xmlrpcBoolean;
1558 if ($this->mytype == 1) {
1559 echo '<B>xmlrpcval</B>: scalar can have only one value<BR>';
1562 $typeof = $xmlrpcTypes[$type];
1564 echo '<B>xmlrpcval</B>: not a scalar type (${typeof})<BR>';
1568 if ($type == $xmlrpcBoolean) {
1569 if (strcasecmp($val, 'true') == 0 || $val == 1 || ($val == true && strcasecmp($val, 'false'))) {
1576 if ($this->mytype == 2) {
1577 // we're adding to an array here
1578 $ar = $this->me['array'];
1579 $ar[] = new xmlrpcval($val, $type);
1580 $this->me['array'] = $ar;
1582 // a scalar, so set the value and remember we're scalar
1583 $this->me[$type] = $val;
1584 $this->mytype = $typeof;
1589 function addArray($vals)
1591 global $xmlrpcTypes;
1592 if ($this->mytype != 0) {
1593 echo '<B>xmlrpcval</B>: already initialized as a [' . $this->kindOf() . ']<BR>';
1597 $this->mytype = $xmlrpcTypes['array'];
1598 $this->me['array'] = $vals;
1602 function addStruct($vals)
1604 global $xmlrpcTypes;
1605 if ($this->mytype != 0) {
1606 echo '<B>xmlrpcval</B>: already initialized as a [' . $this->kindOf() . ']<BR>';
1609 $this->mytype = $xmlrpcTypes['struct'];
1610 $this->me['struct'] = $vals;
1617 while (list($key, $val) = each($ar)) {
1618 echo "$key => $val<br>";
1619 if ($key == 'array') {
1620 while (list($key2, $val2) = each($val)) {
1621 echo "-- $key2 => $val2<br>";
1629 switch ($this->mytype) {
1644 function serializedata($typ, $val)
1647 global $xmlrpcTypes, $xmlrpcBase64, $xmlrpcString,
1649 switch (@$xmlrpcTypes[$typ]) {
1652 $rs .= "<struct>\n";
1654 while (list($key2, $val2) = each($val)) {
1655 $rs .= "<member><name>${key2}</name>\n";
1656 $rs .= $this->serializeval($val2);
1657 $rs .= "</member>\n";
1663 $rs .= "<array>\n<data>\n";
1664 for ($i = 0; $i < sizeof($val); $i++) {
1665 $rs .= $this->serializeval($val[$i]);
1667 $rs .= "</data>\n</array>";
1672 $rs .= "<${typ}>" . base64_encode($val) . "</${typ}>";
1674 case $xmlrpcBoolean:
1675 $rs .= "<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
1678 // G. Giunta 2005/2/13: do NOT use htmlentities, since
1679 // it will produce named html entities, which are invalid xml
1680 $rs .= "<${typ}>" . xmlrpc_encode_entitites($val) . "</${typ}>";
1681 // $rs.="<${typ}>" . htmlentities($val). "</${typ}>";
1684 $rs .= "<${typ}>${val}</${typ}>";
1693 function serialize()
1695 return $this->serializeval($this);
1698 function serializeval($o)
1700 //global $xmlrpcTypes;
1704 list($typ, $val) = each($ar);
1706 $rs .= $this->serializedata($typ, $val);
1707 $rs .= "</value>\n";
1711 function structmem($m)
1713 $nv = $this->me['struct'][$m];
1717 function structreset()
1719 reset($this->me['struct']);
1722 function structeach()
1724 return each($this->me['struct']);
1730 global $xmlrpcBoolean, $xmlrpcBase64;
1732 list($a, $b) = each($this->me);
1733 // contributed by I Sofer, 2001-03-24
1734 // add support for nested arrays to scalarval
1735 // i've created a new method here, so as to
1736 // preserve back compatibility
1740 while (list($id, $cont) = @each($b)) {
1741 $b[$id] = $cont->scalarval();
1745 // add support for structures directly encoding php objects
1746 if (is_object($b)) {
1747 $t = get_object_vars($b);
1749 while (list($id, $cont) = @each($t)) {
1750 $t[$id] = $cont->scalarval();
1753 while (list($id, $cont) = @each($t)) {
1754 //eval('$b->'.$id.' = $cont;');
1762 function scalarval()
1764 //global $xmlrpcBoolean, $xmlrpcBase64;
1766 list($a, $b) = each($this->me);
1770 function scalartyp()
1772 global $xmlrpcI4, $xmlrpcInt;
1774 list($a, $b) = each($this->me);
1775 if ($a == $xmlrpcI4) {
1781 function arraymem($m)
1783 $nv = $this->me['array'][$m];
1787 function arraysize()
1790 list($a, $b) = each($this->me);
1796 function iso8601_encode($timet, $utc = 0)
1798 // return an ISO8601 encoded string
1799 // really, timezones ought to be supported
1800 // but the XML-RPC spec says:
1802 // "Don't assume a timezone. It should be specified by the server in its
1803 // documentation what assumptions it makes about timezones."
1805 // these routines always assume localtime unless
1806 // $utc is set to 1, in which case UTC is assumed
1807 // and an adjustment for locale is made when encoding
1809 $t = strftime("%Y%m%dT%H:%M:%S", $timet);
1811 if (function_exists('gmstrftime')) {
1812 // gmstrftime doesn't exist in some versions
1814 $t = gmstrftime("%Y%m%dT%H:%M:%S", $timet);
1816 $t = strftime("%Y%m%dT%H:%M:%S", $timet - date('Z'));
1822 function iso8601_decode($idate, $utc = 0)
1824 // return a timet in the localtime, or UTC
1826 if (ereg("([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})", $idate, $regs)) {
1828 $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1830 $t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
1836 /****************************************************************
1837 * xmlrpc_decode takes a message in PHP xmlrpc object format and *
1838 * tranlates it into native PHP types. *
1840 * author: Dan Libby (dan@libby.com) *
1841 ****************************************************************/
1842 function php_xmlrpc_decode($xmlrpc_val)
1844 $kind = $xmlrpc_val->kindOf();
1846 if ($kind == 'scalar') {
1847 return $xmlrpc_val->scalarval();
1848 } elseif ($kind == 'array') {
1849 $size = $xmlrpc_val->arraysize();
1852 for ($i = 0; $i < $size; $i++) {
1853 $arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i));
1856 } elseif ($kind == 'struct') {
1857 $xmlrpc_val->structreset();
1860 while (list($key, $value) = $xmlrpc_val->structeach()) {
1861 $arr[$key] = php_xmlrpc_decode($value);
1867 if (function_exists('xmlrpc_decode')) {
1868 define('XMLRPC_EPI_ENABLED', '1');
1870 define('XMLRPC_EPI_ENABLED', '0');
1871 function xmlrpc_decode($xmlrpc_val)
1873 $kind = $xmlrpc_val->kindOf();
1875 if ($kind == 'scalar') {
1876 return $xmlrpc_val->scalarval();
1877 } elseif ($kind == 'array') {
1878 $size = $xmlrpc_val->arraysize();
1881 for ($i = 0; $i < $size; $i++) {
1882 $arr[] = xmlrpc_decode($xmlrpc_val->arraymem($i));
1885 } elseif ($kind == 'struct') {
1886 $xmlrpc_val->structreset();
1889 while (list($key, $value) = $xmlrpc_val->structeach()) {
1890 $arr[$key] = xmlrpc_decode($value);
1897 /****************************************************************
1898 * xmlrpc_encode takes native php types and encodes them into *
1899 * xmlrpc PHP object format. *
1900 * BUG: All sequential arrays are turned into structs. I don't *
1901 * know of a good way to determine if an array is sequential *
1904 * feature creep -- could support more types via optional type *
1907 * author: Dan Libby (dan@libby.com) *
1908 ****************************************************************/
1909 function php_xmlrpc_encode($php_val)
1912 global $xmlrpcDouble;
1913 global $xmlrpcString;
1914 global $xmlrpcArray;
1915 global $xmlrpcStruct;
1916 global $xmlrpcBoolean;
1918 $type = gettype($php_val);
1919 $xmlrpc_val = new xmlrpcval;
1925 while (list($k, $v) = each($php_val)) {
1926 $arr[$k] = php_xmlrpc_encode($v);
1928 $xmlrpc_val->addStruct($arr);
1931 $xmlrpc_val->addScalar($php_val, $xmlrpcInt);
1934 $xmlrpc_val->addScalar($php_val, $xmlrpcDouble);
1937 $xmlrpc_val->addScalar($php_val, $xmlrpcString);
1939 // <G_Giunta_2001-02-29>
1940 // Add support for encoding/decoding of booleans, since they are supported in PHP
1942 $xmlrpc_val->addScalar($php_val, $xmlrpcBoolean);
1944 // </G_Giunta_2001-02-29>
1945 // catch "resource", "NULL", "user function", "unknown type"
1946 //case 'unknown type':
1948 // giancarlo pinerolo <ping@alt.it>
1950 // an empty object in case (which is already
1951 // at this point), not a boolean.
1957 if (XMLRPC_EPI_ENABLED == '0') {
1958 function xmlrpc_encode($php_val)
1961 global $xmlrpcDouble;
1962 global $xmlrpcString;
1963 global $xmlrpcArray;
1964 global $xmlrpcStruct;
1965 global $xmlrpcBoolean;
1967 $type = gettype($php_val);
1968 $xmlrpc_val = new xmlrpcval;
1974 while (list($k, $v) = each($php_val)) {
1975 $arr[$k] = xmlrpc_encode($v);
1977 $xmlrpc_val->addStruct($arr);
1980 $xmlrpc_val->addScalar($php_val, $xmlrpcInt);
1983 $xmlrpc_val->addScalar($php_val, $xmlrpcDouble);
1986 $xmlrpc_val->addScalar($php_val, $xmlrpcString);
1988 // <G_Giunta_2001-02-29>
1989 // Add support for encoding/decoding of booleans, since they are supported in PHP
1991 $xmlrpc_val->addScalar($php_val, $xmlrpcBoolean);
1993 // </G_Giunta_2001-02-29>
1994 //case 'unknown type':
1996 // giancarlo pinerolo <ping@alt.it>
1998 // an empty object in case (which is already
1999 // at this point), not a boolean.