2 // by Edd Dumbill (C) 1999-2002
\r
3 // <edd@usefulinc.com>
\r
4 // $Id: xmlrpc.inc.php,v 1.4.4.2 2005-08-16 05:26:18 kimitake Exp $
\r
5 // $NucleusJP: xmlrpc.inc.php,v 1.4.2.2 2005/07/03 05:16:44 kimitake Exp $
\r
7 // Copyright (c) 1999,2000,2002 Edd Dumbill.
\r
8 // All rights reserved.
\r
10 // Redistribution and use in source and binary forms, with or without
\r
11 // modification, are permitted provided that the following conditions
\r
14 // * Redistributions of source code must retain the above copyright
\r
15 // notice, this list of conditions and the following disclaimer.
\r
17 // * Redistributions in binary form must reproduce the above
\r
18 // copyright notice, this list of conditions and the following
\r
19 // disclaimer in the documentation and/or other materials provided
\r
20 // with the distribution.
\r
22 // * Neither the name of the "XML-RPC for PHP" nor the names of its
\r
23 // contributors may be used to endorse or promote products derived
\r
24 // from this software without specific prior written permission.
\r
26 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
\r
27 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
\r
28 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
\r
29 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
\r
30 // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
\r
31 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
\r
32 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
\r
33 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
\r
34 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
\r
35 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
\r
36 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
\r
37 // OF THE POSSIBILITY OF SUCH DAMAGE.
\r
39 if (!function_exists('xml_parser_create'))
\r
41 // Win 32 fix. From: 'Leo West' <lwest@imaginet.fr>
\r
52 // G. Giunta 2005/01/29: declare global these variables,
\r
53 // so that xmlrpc.inc will work even if included from within a function
\r
54 // NB: it will give warnings in PHP3, so we comment it out
\r
55 // Milosch: Next round, maybe we should explicitly request these via $GLOBALS where used.
\r
56 if (phpversion() >= '4')
\r
60 global $xmlrpcDouble;
\r
61 global $xmlrpcBoolean;
\r
62 global $xmlrpcString;
\r
63 global $xmlrpcDateTime;
\r
64 global $xmlrpcBase64;
\r
65 global $xmlrpcArray;
\r
66 global $xmlrpcStruct;
\r
68 global $xmlrpcTypes;
\r
69 global $xmlEntities;
\r
72 global $xmlrpc_defencoding;
\r
73 global $xmlrpc_internalencoding;
\r
75 global $xmlrpcVersion;
\r
76 global $xmlrpcerruser;
\r
77 global $xmlrpcerrxml;
\r
78 global $xmlrpc_backslash;
\r
83 $xmlrpcBoolean='boolean';
\r
84 $xmlrpcDouble='double';
\r
85 $xmlrpcString='string';
\r
86 $xmlrpcDateTime='dateTime.iso8601';
\r
87 $xmlrpcBase64='base64';
\r
88 $xmlrpcArray='array';
\r
89 $xmlrpcStruct='struct';
\r
94 $xmlrpcBoolean => 1,
\r
97 $xmlrpcDateTime => 1,
\r
103 $xmlrpc_valid_parents = array(
\r
104 'BOOLEAN' => array('VALUE'),
\r
105 'I4' => array('VALUE'),
\r
106 'INT' => array('VALUE'),
\r
107 'STRING' => array('VALUE'),
\r
108 'DOUBLE' => array('VALUE'),
\r
109 'DATETIME.ISO8601' => array('VALUE'),
\r
110 'BASE64' => array('VALUE'),
\r
111 'ARRAY' => array('VALUE'),
\r
112 'STRUCT' => array('VALUE'),
\r
113 'PARAM' => array('PARAMS'),
\r
114 'METHODNAME' => array('METHODCALL'),
\r
115 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
\r
116 'MEMBER' => array('STRUCT'),
\r
117 'NAME' => array('MEMBER'),
\r
118 'DATA' => array('ARRAY'),
\r
119 'FAULT' => array('METHODRESPONSE'),
\r
120 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
\r
123 $xmlEntities=array(
\r
131 $xmlrpcerr['unknown_method']=1;
\r
132 $xmlrpcstr['unknown_method']='Unknown method';
\r
133 $xmlrpcerr['invalid_return']=2;
\r
134 $xmlrpcstr['invalid_return']='Invalid return payload: enable debugging to examine incoming payload';
\r
135 $xmlrpcerr['incorrect_params']=3;
\r
136 $xmlrpcstr['incorrect_params']='Incorrect parameters passed to method';
\r
137 $xmlrpcerr['introspect_unknown']=4;
\r
138 $xmlrpcstr['introspect_unknown']="Can't introspect: method unknown";
\r
139 $xmlrpcerr['http_error']=5;
\r
140 $xmlrpcstr['http_error']="Didn't receive 200 OK from remote server.";
\r
141 $xmlrpcerr['no_data']=6;
\r
142 $xmlrpcstr['no_data']='No data received from server.';
\r
143 $xmlrpcerr['no_ssl']=7;
\r
144 $xmlrpcstr['no_ssl']='No SSL support compiled in.';
\r
145 $xmlrpcerr['curl_fail']=8;
\r
146 $xmlrpcstr['curl_fail']='CURL error';
\r
147 $xmlrpcerr['invalid_request']=15;
\r
148 $xmlrpcstr['invalid_request']='Invalid request payload';
\r
150 $xmlrpcerr['multicall_notstruct'] = 9;
\r
151 $xmlrpcstr['multicall_notstruct'] = 'system.multicall expected struct';
\r
152 $xmlrpcerr['multicall_nomethod'] = 10;
\r
153 $xmlrpcstr['multicall_nomethod'] = 'missing methodName';
\r
154 $xmlrpcerr['multicall_notstring'] = 11;
\r
155 $xmlrpcstr['multicall_notstring'] = 'methodName is not a string';
\r
156 $xmlrpcerr['multicall_recursion'] = 12;
\r
157 $xmlrpcstr['multicall_recursion'] = 'recursive system.multicall forbidden';
\r
158 $xmlrpcerr['multicall_noparams'] = 13;
\r
159 $xmlrpcstr['multicall_noparams'] = 'missing params';
\r
160 $xmlrpcerr['multicall_notarray'] = 14;
\r
161 $xmlrpcstr['multicall_notarray'] = 'params is not an array';
\r
163 // The charset encoding expected by the server for received messages and
\r
164 // by the client for received responses
\r
165 $xmlrpc_defencoding='UTF-8';
\r
166 // The encoding used by PHP.
\r
167 // String values received will be converted to this.
\r
168 $xmlrpc_internalencoding='ISO-8859-1';
\r
170 $xmlrpcName='XML-RPC for PHP';
\r
171 $xmlrpcVersion='1.2';
\r
173 // let user errors start at 800
\r
174 $xmlrpcerruser=800;
\r
175 // let XML parse errors start at 100
\r
178 // formulate backslashes for escaping regexp
\r
179 $xmlrpc_backslash=chr(92).chr(92);
\r
181 // used to store state during parsing
\r
182 // quick explanation of components:
\r
183 // ac - used to accumulate values
\r
184 // isf - used to indicate a fault
\r
185 // lv - used to indicate "looking for a value": implements
\r
186 // the logic to allow values with no types to be strings
\r
187 // params - used to store parameters in method calls
\r
188 // method - used to store method name
\r
189 // stack - array with genealogy of xml elements names:
\r
190 // used to validate nesting of xmlrpc elements
\r
195 * To help correct communication of non-ascii chars inside strings, regardless
\r
196 * of the charset used when sending requests, parsing them, sending responses
\r
197 * and parsing responses, convert all non-ascii chars present in the message
\r
198 * into their equivalent 'charset entity'. Charset entities enumerated this way
\r
199 * are independent of the charset encoding used to transmit them, and all XML
\r
200 * parsers are bound to understand them.
\r
202 function xmlrpc_entity_decode($string)
\r
204 $top=split('&', $string);
\r
207 while($i<sizeof($top))
\r
209 if (ereg("^([#a-zA-Z0-9]+);", $top[$i], $regs))
\r
211 $op.=ereg_replace("^[#a-zA-Z0-9]+;",
\r
212 xmlrpc_lookup_entity($regs[1]),
\r
223 $op.='&' . $top[$i];
\r
231 function xmlrpc_lookup_entity($ent)
\r
233 global $xmlEntities;
\r
235 if (isset($xmlEntities[strtolower($ent)]))
\r
237 return $xmlEntities[strtolower($ent)];
\r
239 if (ereg("^#([0-9]+)$", $ent, $regs))
\r
241 return chr($regs[1]);
\r
247 * These entities originate from HTML specs (1.1, proposed 2.0, etc),
\r
248 * and are taken directly from php-4.3.1/ext/mbstring/html_entities.c.
\r
249 * Until php provides functionality to translate these entities in its
\r
250 * core library, use this function.
\r
252 function xmlrpc_html_entity_xlate($data = '')
\r
255 " " => " ",
\r
256 "¡" => "¡",
\r
257 "¢" => "¢",
\r
258 "£" => "£",
\r
259 "¤" => "¤",
\r
260 "¥" => "¥",
\r
261 "¦" => "¦",
\r
262 "§" => "§",
\r
263 "¨" => "¨",
\r
264 "©" => "©",
\r
265 "ª" => "ª",
\r
266 "«" => "«",
\r
267 "¬" => "¬",
\r
268 "­" => "­",
\r
269 "®" => "®",
\r
270 "¯" => "¯",
\r
271 "°" => "°",
\r
272 "±" => "±",
\r
273 "²" => "²",
\r
274 "³" => "³",
\r
275 "´" => "´",
\r
276 "µ" => "µ",
\r
277 "¶" => "¶",
\r
278 "·" => "·",
\r
279 "¸" => "¸",
\r
280 "¹" => "¹",
\r
281 "º" => "º",
\r
282 "»" => "»",
\r
283 "¼" => "¼",
\r
284 "½" => "½",
\r
285 "¾" => "¾",
\r
286 "¿" => "¿",
\r
287 "À" => "À",
\r
288 "Á" => "Á",
\r
289 "Â" => "Â",
\r
290 "Ã" => "Ã",
\r
291 "Ä" => "Ä",
\r
292 "Å" => "Å",
\r
293 "Æ" => "Æ",
\r
294 "Ç" => "Ç",
\r
295 "È" => "È",
\r
296 "É" => "É",
\r
297 "Ê" => "Ê",
\r
298 "Ë" => "Ë",
\r
299 "Ì" => "Ì",
\r
300 "Í" => "Í",
\r
301 "Î" => "Î",
\r
302 "Ï" => "Ï",
\r
303 "Ð" => "Ð",
\r
304 "Ñ" => "Ñ",
\r
305 "Ò" => "Ò",
\r
306 "Ó" => "Ó",
\r
307 "Ô" => "Ô",
\r
308 "Õ" => "Õ",
\r
309 "Ö" => "Ö",
\r
310 "×" => "×",
\r
311 "Ø" => "Ø",
\r
312 "Ù" => "Ù",
\r
313 "Ú" => "Ú",
\r
314 "Û" => "Û",
\r
315 "Ü" => "Ü",
\r
316 "Ý" => "Ý",
\r
317 "Þ" => "Þ",
\r
318 "ß" => "ß",
\r
319 "à" => "à",
\r
320 "á" => "á",
\r
321 "â" => "â",
\r
322 "ã" => "ã",
\r
323 "ä" => "ä",
\r
324 "å" => "å",
\r
325 "æ" => "æ",
\r
326 "ç" => "ç",
\r
327 "è" => "è",
\r
328 "é" => "é",
\r
329 "ê" => "ê",
\r
330 "ë" => "ë",
\r
331 "ì" => "ì",
\r
332 "í" => "í",
\r
333 "î" => "î",
\r
334 "ï" => "ï",
\r
335 "ð" => "ð",
\r
336 "ñ" => "ñ",
\r
337 "ò" => "ò",
\r
338 "ó" => "ó",
\r
339 "ô" => "ô",
\r
340 "õ" => "õ",
\r
341 "ö" => "ö",
\r
342 "÷" => "÷",
\r
343 "ø" => "ø",
\r
344 "ù" => "ù",
\r
345 "ú" => "ú",
\r
346 "û" => "û",
\r
347 "ü" => "ü",
\r
348 "ý" => "ý",
\r
349 "þ" => "þ",
\r
350 "ÿ" => "ÿ",
\r
351 "Œ" => "Œ",
\r
352 "œ" => "œ",
\r
353 "Š" => "Š",
\r
354 "š" => "š",
\r
355 "Ÿ" => "Ÿ",
\r
356 "ƒ" => "ƒ",
\r
357 "ˆ" => "ˆ",
\r
358 "˜" => "˜",
\r
359 "Α" => "Α",
\r
360 "Β" => "Β",
\r
361 "Γ" => "Γ",
\r
362 "Δ" => "Δ",
\r
363 "Ε" => "Ε",
\r
364 "Ζ" => "Ζ",
\r
365 "Η" => "Η",
\r
366 "Θ" => "Θ",
\r
367 "Ι" => "Ι",
\r
368 "Κ" => "Κ",
\r
369 "Λ" => "Λ",
\r
370 "Μ" => "Μ",
\r
371 "Ν" => "Ν",
\r
372 "Ξ" => "Ξ",
\r
373 "Ο" => "Ο",
\r
374 "Π" => "Π",
\r
375 "Ρ" => "Ρ",
\r
376 "Σ" => "Σ",
\r
377 "Τ" => "Τ",
\r
378 "Υ" => "Υ",
\r
379 "Φ" => "Φ",
\r
380 "Χ" => "Χ",
\r
381 "Ψ" => "Ψ",
\r
382 "Ω" => "Ω",
\r
383 "β" => "β",
\r
384 "γ" => "γ",
\r
385 "δ" => "δ",
\r
386 "ε" => "ε",
\r
387 "ζ" => "ζ",
\r
388 "η" => "η",
\r
389 "θ" => "θ",
\r
390 "ι" => "ι",
\r
391 "κ" => "κ",
\r
392 "λ" => "λ",
\r
393 "μ" => "μ",
\r
394 "ν" => "ν",
\r
395 "ξ" => "ξ",
\r
396 "ο" => "ο",
\r
397 "π" => "π",
\r
398 "ρ" => "ρ",
\r
399 "ς" => "ς",
\r
400 "σ" => "σ",
\r
401 "τ" => "τ",
\r
402 "υ" => "υ",
\r
403 "φ" => "φ",
\r
404 "χ" => "χ",
\r
405 "ψ" => "ψ",
\r
406 "ω" => "ω",
\r
407 "ϑ" => "ϑ",
\r
408 "ϒ" => "ϒ",
\r
409 "ϖ" => "ϖ",
\r
410 " " => " ",
\r
411 " " => " ",
\r
412 " " => " ",
\r
413 "‌" => "‌",
\r
414 "‍" => "‍",
\r
415 "‎" => "‎",
\r
416 "‏" => "‏",
\r
417 "–" => "–",
\r
418 "—" => "—",
\r
419 "‘" => "‘",
\r
420 "’" => "’",
\r
421 "‚" => "‚",
\r
422 "“" => "“",
\r
423 "”" => "”",
\r
424 "„" => "„",
\r
425 "†" => "†",
\r
426 "‡" => "‡",
\r
427 "•" => "•",
\r
428 "…" => "…",
\r
429 "‰" => "‰",
\r
430 "′" => "′",
\r
431 "″" => "″",
\r
432 "‹" => "‹",
\r
433 "›" => "›",
\r
434 "‾" => "‾",
\r
435 "⁄" => "⁄",
\r
436 "€" => "€",
\r
437 "℘" => "℘",
\r
438 "ℑ" => "ℑ",
\r
439 "ℜ" => "ℜ",
\r
440 "™" => "™",
\r
441 "ℵ" => "ℵ",
\r
442 "←" => "←",
\r
443 "↑" => "↑",
\r
444 "→" => "→",
\r
445 "↓" => "↓",
\r
446 "↔" => "↔",
\r
447 "↵" => "↵",
\r
448 "⇐" => "⇐",
\r
449 "⇑" => "⇑",
\r
450 "⇒" => "⇒",
\r
451 "⇓" => "⇓",
\r
452 "⇔" => "⇔",
\r
453 "∀" => "∀",
\r
454 "∂" => "∂",
\r
455 "∃" => "∃",
\r
456 "∅" => "∅",
\r
457 "∇" => "∇",
\r
458 "∈" => "∈",
\r
459 "∉" => "∉",
\r
460 "∋" => "∋",
\r
461 "∏" => "∏",
\r
462 "∑" => "∑",
\r
463 "−" => "−",
\r
464 "∗" => "∗",
\r
465 "√" => "√",
\r
466 "∝" => "∝",
\r
467 "∞" => "∞",
\r
468 "∠" => "∠",
\r
469 "∧" => "∧",
\r
470 "∨" => "∨",
\r
471 "∩" => "∩",
\r
472 "∪" => "∪",
\r
473 "∫" => "∫",
\r
474 "∴" => "∴",
\r
475 "∼" => "∼",
\r
476 "≅" => "≅",
\r
477 "≈" => "≈",
\r
478 "≠" => "≠",
\r
479 "≡" => "≡",
\r
480 "≤" => "≤",
\r
481 "≥" => "≥",
\r
482 "⊂" => "⊂",
\r
483 "⊃" => "⊃",
\r
484 "⊄" => "⊄",
\r
485 "⊆" => "⊆",
\r
486 "⊇" => "⊇",
\r
487 "⊕" => "⊕",
\r
488 "⊗" => "⊗",
\r
489 "⊥" => "⊥",
\r
490 "⋅" => "⋅",
\r
491 "⌈" => "⌈",
\r
492 "⌉" => "⌉",
\r
493 "⌊" => "⌊",
\r
494 "⌋" => "⌋",
\r
495 "⟨" => "〈",
\r
496 "⟩" => "〉",
\r
497 "◊" => "◊",
\r
498 "♠" => "♠",
\r
499 "♣" => "♣",
\r
500 "♥" => "♥",
\r
501 "♦" => "♦");
\r
502 return strtr($data, $entities);
\r
505 function xmlrpc_encode_entitites($data)
\r
507 $length = strlen($data);
\r
509 for($position = 0; $position < $length; $position++)
\r
511 $character = substr($data, $position, 1);
\r
512 $code = Ord($character);
\r
515 $character = """;
\r
518 $character = "&";
\r
521 $character = "'";
\r
524 $character = "<";
\r
527 $character = ">";
\r
530 if ($code < 32 || $code > 159)
\r
531 $character = ("&#".strval($code).";");
\r
534 $escapeddata .= $character;
\r
536 return $escapeddata;
\r
539 function xmlrpc_se($parser, $name, $attrs)
\r
541 global $_xh, $xmlrpcDateTime, $xmlrpcString, $xmlrpc_valid_parents;
\r
543 // if invalid xmlrpc already detected, skip all processing
\r
544 if ($_xh[$parser]['isf'] < 2)
\r
547 // check for correct element nesting
\r
548 // top level element can only be of 2 types
\r
549 if (count($_xh[$parser]['stack']) == 0)
\r
551 if ($name != 'METHODRESPONSE' && $name != 'METHODCALL')
\r
553 $_xh[$parser]['isf'] = 2;
\r
554 $_xh[$parser]['isf_reason'] = 'missing top level xmlrpc element';
\r
560 // not top level element: see if parent is OK
\r
561 if (!in_array($_xh[$parser]['stack'][0], $xmlrpc_valid_parents[$name]))
\r
563 $_xh[$parser]['isf'] = 2;
\r
564 $_xh[$parser]['isf_reason'] = "xmlrpc element $name cannot be child of {$_xh[$parser]['stack'][0]}";
\r
573 //$_xh[$parser]['st'].='array(';
\r
574 //$_xh[$parser]['cm']++;
\r
575 // this last line turns quoting off
\r
576 // this means if we get an empty array we'll
\r
577 // simply get a bit of whitespace in the eval
\r
578 //$_xh[$parser]['qt']=0;
\r
580 // create an empty array to hold child values, and push it onto appropriate stack
\r
581 $cur_val = array();
\r
582 $cur_val['values'] = array();
\r
583 $cur_val['type'] = $name;
\r
584 array_unshift($_xh[$parser]['valuestack'], $cur_val);
\r
588 //$_xh[$parser]['st'].='"';
\r
589 $_xh[$parser]['ac']='';
\r
592 $_xh[$parser]['isf']=1;
\r
595 //$_xh[$parser]['st']='';
\r
596 // clear value, so we can check later if no value will passed for this param/member
\r
597 $_xh[$parser]['value']=null;
\r
600 //$_xh[$parser]['st'].='new xmlrpcval(';
\r
601 // look for a value: if this is still true by the
\r
602 // time we reach the end tag for value then the type is string
\r
604 $_xh[$parser]['vt']='value';
\r
605 $_xh[$parser]['ac']='';
\r
606 //$_xh[$parser]['qt']=0;
\r
607 $_xh[$parser]['lv']=1;
\r
614 case 'DATETIME.ISO8601':
\r
616 if ($_xh[$parser]['vt']!='value')
\r
618 //two data elements inside a value: an error occurred!
\r
619 $_xh[$parser]['isf'] = 2;
\r
620 $_xh[$parser]['isf_reason'] = "$name element following a {$_xh[$parser]['vt']} element inside a single value";
\r
624 // reset the accumulator
\r
625 $_xh[$parser]['ac']='';
\r
627 /*if ($name=='DATETIME.ISO8601' || $name=='STRING')
\r
629 $_xh[$parser]['qt']=1;
\r
630 if ($name=='DATETIME.ISO8601')
\r
632 $_xh[$parser]['vt']=$xmlrpcDateTime;
\r
635 elseif ($name=='BASE64')
\r
637 $_xh[$parser]['qt']=2;
\r
641 // No quoting is required here -- but
\r
642 // at the end of the element we must check
\r
643 // for data format errors.
\r
644 $_xh[$parser]['qt']=0;
\r
648 //$_xh[$parser]['ac']='';
\r
649 // avoid warnings later on if no NAME is found before VALUE inside
\r
650 // a struct member predefining member name as NULL
\r
651 $_xh[$parser]['valuestack'][0]['name'] = '';
\r
652 // clear value, so we can check later if no value will passed for this param/member
\r
653 $_xh[$parser]['value']=null;
\r
657 case 'METHODRESPONSE':
\r
659 // valid elements that add little to processing
\r
662 /// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
\r
663 $_xh[$parser]['isf'] = 2;
\r
664 $_xh[$parser]['isf_reason'] = "found not-xmlrpc xml element $name";
\r
668 // Save current element name to stack, to validate nesting
\r
669 array_unshift($_xh[$parser]['stack'], $name);
\r
671 if ($name!='VALUE')
\r
673 $_xh[$parser]['lv']=0;
\r
678 function xmlrpc_ee($parser, $name)
\r
680 global $_xh,$xmlrpcTypes,$xmlrpcString,$xmlrpcDateTime;
\r
682 if ($_xh[$parser]['isf'] < 2)
\r
685 // push this element name from stack
\r
686 // NB: if XML validates, correct opening/closing is guaranteed and
\r
687 // we do not have to check for $name == $curr_elem.
\r
688 // we also checked for proper nesting at start of elements...
\r
689 $curr_elem = array_shift($_xh[$parser]['stack']);
\r
695 //if ($_xh[$parser]['cm'] && substr($_xh[$parser]['st'], -1) ==',')
\r
697 // $_xh[$parser]['st']=substr($_xh[$parser]['st'],0,-1);
\r
699 //$_xh[$parser]['st'].=')';
\r
701 // fetch out of stack array of values, and promote it to current value
\r
702 $cur_val = array_shift($_xh[$parser]['valuestack']);
\r
703 $_xh[$parser]['value'] = $cur_val['values'];
\r
705 $_xh[$parser]['vt']=strtolower($name);
\r
706 //$_xh[$parser]['cm']--;
\r
709 //$_xh[$parser]['st'].= $_xh[$parser]['ac'] . '" => ';
\r
710 $_xh[$parser]['valuestack'][0]['name'] = $_xh[$parser]['ac'];
\r
717 case 'DATETIME.ISO8601':
\r
719 $_xh[$parser]['vt']=strtolower($name);
\r
720 //if ($_xh[$parser]['qt']==1)
\r
721 if ($name=='STRING')
\r
723 // we use double quotes rather than single so backslashification works OK
\r
724 //$_xh[$parser]['st'].='"'. $_xh[$parser]['ac'] . '"';
\r
725 $_xh[$parser]['value']=$_xh[$parser]['ac'];
\r
727 elseif ($name=='DATETIME.ISO8601')
\r
729 $_xh[$parser]['vt']=$xmlrpcDateTime;
\r
730 $_xh[$parser]['value']=$_xh[$parser]['ac'];
\r
732 elseif ($name=='BASE64')
\r
734 //$_xh[$parser]['st'].='base64_decode("'. $_xh[$parser]['ac'] . '")';
\r
736 ///@todo check for failure of base64 decoding / catch warnings
\r
737 $_xh[$parser]['value']=base64_decode($_xh[$parser]['ac']);
\r
739 elseif ($name=='BOOLEAN')
\r
741 // special case here: we translate boolean 1 or 0 into PHP
\r
742 // constants true or false
\r
743 // NB: this simple checks helps a lot sanitizing input, ie no
\r
744 // security problems around here
\r
745 if ($_xh[$parser]['ac']=='1')
\r
747 //$_xh[$parser]['ac']='true';
\r
748 $_xh[$parser]['value']=true;
\r
752 //$_xh[$parser]['ac']='false';
\r
753 // log if receiveing something strange, even though we set the value to false anyway
\r
754 if ($_xh[$parser]['ac']!='0')
\r
755 error_log('XML-RPC: invalid value received in BOOLEAN: '.$_xh[$parser]['ac']);
\r
756 $_xh[$parser]['value']=false;
\r
758 //$_xh[$parser]['st'].=$_xh[$parser]['ac'];
\r
760 elseif ($name=='DOUBLE')
\r
762 // we have a DOUBLE
\r
763 // we must check that only 0123456789-.<space> are characters here
\r
764 if (!ereg("^[+-]?[eE0123456789 \\t\\.]+$", $_xh[$parser]['ac']))
\r
766 // TODO: find a better way of throwing an error
\r
768 error_log('XML-RPC: non numeric value received in DOUBLE: '.$_xh[$parser]['ac']);
\r
769 //$_xh[$parser]['st'].="'ERROR_NON_NUMERIC_FOUND'";
\r
770 $_xh[$parser]['value']='ERROR_NON_NUMERIC_FOUND';
\r
774 // it's ok, add it on
\r
775 //$_xh[$parser]['st'].=(double)$_xh[$parser]['ac'];
\r
776 $_xh[$parser]['value']=(double)$_xh[$parser]['ac'];
\r
781 // we have an I4/INT
\r
782 // we must check that only 0123456789-<space> are characters here
\r
783 if (!ereg("^[+-]?[0123456789 \\t]+$", $_xh[$parser]['ac']))
\r
785 // TODO: find a better way of throwing an error
\r
787 error_log('XML-RPC: non numeric value received in INT: '.$_xh[$parser]['ac']);
\r
788 //$_xh[$parser]['st'].="'ERROR_NON_NUMERIC_FOUND'";
\r
789 $_xh[$parser]['value']='ERROR_NON_NUMERIC_FOUND';
\r
793 // it's ok, add it on
\r
794 //$_xh[$parser]['st'].=(int)$_xh[$parser]['ac'];
\r
795 $_xh[$parser]['value']=(int)$_xh[$parser]['ac'];
\r
798 $_xh[$parser]['ac']='';
\r
799 //$_xh[$parser]['qt']=0;
\r
800 $_xh[$parser]['lv']=3; // indicate we've found a value
\r
803 // This if() detects if no scalar was inside <VALUE></VALUE>
\r
804 if ($_xh[$parser]['vt']=='value')
\r
806 $_xh[$parser]['value']=$_xh[$parser]['ac'];
\r
807 $_xh[$parser]['vt']=$xmlrpcString;
\r
809 /*if (strlen($_xh[$parser]['ac'])>0 &&
\r
810 $_xh[$parser]['vt']==$xmlrpcString)
\r
812 $_xh[$parser]['st'].='"'. $_xh[$parser]['ac'] . '"';
\r
814 // This if() detects if no scalar was inside <VALUE></VALUE>
\r
815 // and pads an empty ''.
\r
816 if($_xh[$parser]['st'][strlen($_xh[$parser]['st'])-1] == '(')
\r
818 $_xh[$parser]['st'].= '""';
\r
820 // G. Giunta 2005/03/12 save some chars in the reconstruction of string vals...
\r
821 if ($_xh[$parser]['vt'] != $xmlrpcString)
\r
822 $_xh[$parser]['st'].=", '" . $_xh[$parser]['vt'] . "')";
\r
824 $_xh[$parser]['st'].=")";
\r
825 if ($_xh[$parser]['cm'])
\r
827 $_xh[$parser]['st'].=',';
\r
830 // build the xmlrpc val out of the data received, and substitute it
\r
831 $temp = new xmlrpcval($_xh[$parser]['value'], $_xh[$parser]['vt']);
\r
832 // check if we are inside an array or struct:
\r
833 // if value just built is inside an array, let's move it into array on the stack
\r
834 if (count($_xh[$parser]['valuestack']) && $_xh[$parser]['valuestack'][0]['type']=='ARRAY')
\r
836 $_xh[$parser]['valuestack'][0]['values'][] = $temp;
\r
840 $_xh[$parser]['value'] = $temp;
\r
844 $_xh[$parser]['ac']='';
\r
845 //$_xh[$parser]['qt']=0;
\r
846 // add to array in the stack the last element built
\r
847 // unless no VALUE was found
\r
848 if ($_xh[$parser]['value'])
\r
849 $_xh[$parser]['valuestack'][0]['values'][$_xh[$parser]['valuestack'][0]['name']] = $_xh[$parser]['value'];
\r
851 error_log('XML-RPC: missing VALUE inside STRUCT in received xml');
\r
854 $_xh[$parser]['ac']='';
\r
855 //$_xh[$parser]['qt']=0;
\r
858 //$_xh[$parser]['params'][]=$_xh[$parser]['st'];
\r
859 if ($_xh[$parser]['value'])
\r
860 $_xh[$parser]['params'][]=$_xh[$parser]['value'];
\r
862 error_log('XML-RPC: missing VALUE inside PARAM in received xml');
\r
865 $_xh[$parser]['method']=ereg_replace("^[\n\r\t ]+", '', $_xh[$parser]['ac']);
\r
870 case 'METHORESPONSE':
\r
873 // End of INVALID ELEMENT!
\r
874 // shall we add an assert here for unreachable code???
\r
877 // if it's a valid type name, set the type
\r
878 /*if (isset($xmlrpcTypes[strtolower($name)]))
\r
880 $_xh[$parser]['vt']=strtolower($name);
\r
886 function xmlrpc_cd($parser, $data)
\r
888 global $_xh, $xmlrpc_backslash;
\r
890 //if (ereg("^[\n\r \t]+$", $data)) return;
\r
891 // print "adding [${data}]\n";
\r
893 // skip processing if xml fault already detected
\r
894 if ($_xh[$parser]['isf'] < 2)
\r
896 if ($_xh[$parser]['lv']!=3)
\r
898 // "lookforvalue==3" means that we've found an entire value
\r
899 // and should discard any further character data
\r
900 if ($_xh[$parser]['lv']==1)
\r
902 // if we've found text and we're just in a <value> then
\r
903 // turn quoting on, as this will be a string
\r
904 //$_xh[$parser]['qt']=1;
\r
905 // and say we've found a value
\r
906 $_xh[$parser]['lv']=2;
\r
908 if(!@isset($_xh[$parser]['ac']))
\r
910 $_xh[$parser]['ac'] = '';
\r
912 //$_xh[$parser]['ac'].=str_replace('$', '\$', str_replace('"', '\"', str_replace(chr(92),$xmlrpc_backslash, $data)));
\r
913 $_xh[$parser]['ac'].=$data;
\r
918 function xmlrpc_dh($parser, $data)
\r
920 global $_xh, $xmlrpc_backslash;
\r
922 // skip processing if xml fault already detected
\r
923 if ($parser[$_xh]['isf'] < 2)
\r
925 if (substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';')
\r
927 if ($_xh[$parser]['lv']==1)
\r
929 //$_xh[$parser]['qt']=1;
\r
930 $_xh[$parser]['lv']=2;
\r
932 //$_xh[$parser]['ac'].=str_replace('$', '\$', str_replace('"', '\"', str_replace(chr(92),$xmlrpc_backslash, $data)));
\r
933 $_xh[$parser]['ac'].=$data;
\r
938 class xmlrpc_client
\r
952 var $no_multicall=false;
\r
954 function xmlrpc_client($path, $server, $port=0)
\r
956 $this->port=$port; $this->server=$server; $this->path=$path;
\r
959 function setDebug($in)
\r
971 function setCredentials($u, $p)
\r
973 $this->username=$u;
\r
974 $this->password=$p;
\r
977 function setCertificate($cert, $certpass)
\r
979 $this->cert = $cert;
\r
980 $this->certpass = $certpass;
\r
983 function setSSLVerifyPeer($i)
\r
985 $this->verifypeer = $i;
\r
988 function setSSLVerifyHost($i)
\r
990 $this->verifyhost = $i;
\r
993 function send($msg, $timeout=0, $method='http')
\r
995 if (is_array($msg))
\r
997 // $msg is an array of xmlrpcmsg's
\r
998 return $this->multicall($msg, $timeout, $method);
\r
1001 // where msg is an xmlrpcmsg
\r
1002 $msg->debug=$this->debug;
\r
1004 if ($method == 'https')
\r
1006 return $this->sendPayloadHTTPS($msg,
\r
1008 $this->port, $timeout,
\r
1009 $this->username, $this->password,
\r
1015 return $this->sendPayloadHTTP10($msg, $this->server, $this->port,
\r
1016 $timeout, $this->username,
\r
1021 function sendPayloadHTTP10($msg, $server, $port, $timeout=0,$username='', $password='')
\r
1023 global $xmlrpcerr, $xmlrpcstr, $xmlrpcName, $xmlrpcVersion, $xmlrpc_defencoding;
\r
1030 $fp=@fsockopen($server, $port,$this->errno, $this->errstr, $timeout);
\r
1034 $fp=@fsockopen($server, $port,$this->errno, $this->errstr);
\r
1038 if ($timeout>0 && function_exists('stream_set_timeout'))
\r
1039 stream_set_timeout($fp, $timeout);
\r
1043 $this->errstr='Connect error';
\r
1044 $r=new xmlrpcresp(0, $xmlrpcerr['http_error'],$xmlrpcstr['http_error']);
\r
1047 // Only create the payload if it was not created previously
\r
1048 if(empty($msg->payload))
\r
1050 $msg->createPayload();
\r
1053 // thanks to Grant Rauscher <grant7@firstworld.net>
\r
1056 if ($username!='')
\r
1058 $credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
\r
1061 $op= "POST " . $this->path. " HTTP/1.0\r\n" .
\r
1062 "User-Agent: " . $xmlrpcName . " " . $xmlrpcVersion . "\r\n" .
\r
1063 "Host: ". $server . "\r\n" .
\r
1065 "Accept-Charset: " . $xmlrpc_defencoding . "\r\n" .
\r
1066 "Content-Type: text/xml\r\nContent-Length: " .
\r
1067 strlen($msg->payload) . "\r\n\r\n" .
\r
1070 if (!fputs($fp, $op, strlen($op)))
\r
1072 $this->errstr='Write error';
\r
1073 $r=new xmlrpcresp(0, $xmlrpcerr['http_error'], $xmlrpcstr['http_error']);
\r
1076 $resp=$msg->parseResponseFile($fp);
\r
1081 // contributed by Justin Miller <justin@voxel.net>
\r
1082 // requires curl to be built into PHP
\r
1083 function sendPayloadHTTPS($msg, $server, $port, $timeout=0,$username='', $password='', $cert='',$certpass='')
\r
1085 global $xmlrpcerr, $xmlrpcstr, $xmlrpcVersion, $xmlrpc_internalencoding;
\r
1091 // Only create the payload if it was not created previously
\r
1092 if(empty($msg->payload))
\r
1094 $msg->createPayload();
\r
1097 if (!function_exists('curl_init'))
\r
1099 $this->errstr='SSL unavailable on this install';
\r
1100 $r=new xmlrpcresp(0, $xmlrpcerr['no_ssl'], $xmlrpcstr['no_ssl']);
\r
1104 $curl = curl_init('https://' . $server . ':' . $port . $this->path);
\r
1106 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
\r
1107 // results into variable
\r
1110 curl_setopt($curl, CURLOPT_VERBOSE, 1);
\r
1112 curl_setopt($curl, CURLOPT_USERAGENT, 'PHP XMLRPC '.$xmlrpcVersion);
\r
1113 // required for XMLRPC
\r
1114 curl_setopt($curl, CURLOPT_POST, 1);
\r
1116 curl_setopt($curl, CURLOPT_POSTFIELDS, $msg->payload);
\r
1118 curl_setopt($curl, CURLOPT_HEADER, 1);
\r
1119 // return the header too
\r
1120 curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: text/xml', 'Accept-Charset: '.$xmlrpc_internalencoding));
\r
1121 // whether to verify remote host's cert
\r
1122 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
\r
1123 // 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
\r
1124 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
\r
1125 // required for XMLRPC
\r
1128 curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
\r
1130 // timeout is borked
\r
1131 if ($username && $password)
\r
1133 curl_setopt($curl, CURLOPT_USERPWD,"$username:$password");
\r
1138 curl_setopt($curl, CURLOPT_SSLCERT, $cert);
\r
1143 curl_setopt($curl, CURLOPT_SSLCERTPASSWD,$certpass);
\r
1145 // set cert password
\r
1147 $result = curl_exec($curl);
\r
1151 $this->errstr='no response';
\r
1152 $resp=new xmlrpcresp(0, $xmlrpcerr['curl_fail'], $xmlrpcstr['curl_fail']. ': '. curl_error($curl));
\r
1153 curl_close($curl);
\r
1157 curl_close($curl);
\r
1158 $resp = $msg->parseResponse($result);
\r
1163 function multicall($msgs, $timeout=0, $method='http')
\r
1167 if (! $this->no_multicall)
\r
1169 $results = $this->_try_multicall($msgs, $timeout, $method);
\r
1170 /* TODO - this is not php3-friendly */
\r
1171 // if($results !== false)
\r
1172 if(is_array($results))
\r
1174 // Either the system.multicall succeeded, or the send
\r
1175 // failed (e.g. due to HTTP timeout). In either case,
\r
1176 // we're done for now.
\r
1181 // system.multicall unsupported by server,
\r
1182 // don't try it next time...
\r
1183 $this->no_multicall = true;
\r
1187 // system.multicall is unupported by server:
\r
1188 // Emulate multicall via multiple requests
\r
1189 $results = array();
\r
1190 //foreach($msgs as $msg)
\r
1192 while(list(,$msg) = @each($msgs))
\r
1194 $results[] = $this->send($msg, $timeout, $method);
\r
1199 // Attempt to boxcar $msgs via system.multicall.
\r
1200 function _try_multicall($msgs, $timeout, $method)
\r
1202 // Construct multicall message
\r
1204 //foreach($msgs as $msg)
\r
1206 while(list(,$msg) = @each($msgs))
\r
1208 $call['methodName'] = new xmlrpcval($msg->method(),'string');
\r
1209 $numParams = $msg->getNumParams();
\r
1210 $params = array();
\r
1211 for ($i = 0; $i < $numParams; $i++)
\r
1213 $params[$i] = $msg->getParam($i);
\r
1215 $call['params'] = new xmlrpcval($params, 'array');
\r
1216 $calls[] = new xmlrpcval($call, 'struct');
\r
1218 $multicall = new xmlrpcmsg('system.multicall');
\r
1219 $multicall->addParam(new xmlrpcval($calls, 'array'));
\r
1221 // Attempt RPC call
\r
1222 $result = $this->send($multicall, $timeout, $method);
\r
1223 if(!is_object($result))
\r
1225 return ($result || 0); // transport failed
\r
1228 if($result->faultCode() != 0)
\r
1230 return false; // system.multicall failed
\r
1233 // Unpack responses.
\r
1234 $rets = $result->value();
\r
1235 if($rets->kindOf() != 'array')
\r
1237 return false; // bad return type from system.multicall
\r
1239 $numRets = $rets->arraysize();
\r
1240 if($numRets != count($msgs))
\r
1242 return false; // wrong number of return values.
\r
1245 $response = array();
\r
1246 for ($i = 0; $i < $numRets; $i++)
\r
1248 $val = $rets->arraymem($i);
\r
1249 switch ($val->kindOf())
\r
1252 if($val->arraysize() != 1)
\r
1254 return false; // Bad value
\r
1256 // Normal return value
\r
1257 $response[$i] = new xmlrpcresp($val->arraymem(0));
\r
1260 $code = $val->structmem('faultCode');
\r
1261 if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
\r
1265 $str = $val->structmem('faultString');
\r
1266 if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
\r
1270 $response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
\r
1278 } // end class xmlrpc_client
\r
1285 var $hdrs = array();
\r
1287 function xmlrpcresp($val, $fcode = 0, $fstr = '')
\r
1292 $this->errno = $fcode;
\r
1293 $this->errstr = $fstr;
\r
1294 //$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
\r
1296 elseif (!is_object($val))
\r
1298 // programmer error
\r
1299 error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to xmlrpcresp. Defaulting to empty value.");
\r
1300 $this->val = new xmlrpcval();
\r
1305 $this->val = $val;
\r
1309 function faultCode()
\r
1311 return $this->errno;
\r
1314 function faultString()
\r
1316 return $this->errstr;
\r
1321 return $this->val;
\r
1324 function serialize()
\r
1326 $result = "<methodResponse>\n";
\r
1329 // G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
\r
1330 $result .= '<fault>
\r
1334 <name>faultCode</name>
\r
1335 <value><int>' . $this->errno . '</int></value>
\r
1338 <name>faultString</name>
\r
1339 <value><string>' . xmlrpc_encode_entitites($this->errstr) . '</string></value>
\r
1347 $result .= "<params>\n<param>\n" .
\r
1348 $this->val->serialize() .
\r
1349 "</param>\n</params>";
\r
1351 $result .= "\n</methodResponse>";
\r
1360 var $params=array();
\r
1363 function xmlrpcmsg($meth, $pars=0)
\r
1365 $this->methodname=$meth;
\r
1366 if (is_array($pars) && sizeof($pars)>0)
\r
1368 for($i=0; $i<sizeof($pars); $i++)
\r
1370 $this->addParam($pars[$i]);
\r
1375 function xml_header()
\r
1377 return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
\r
1380 function xml_footer()
\r
1382 return "</methodCall>\n";
\r
1385 function createPayload()
\r
1387 $this->payload=$this->xml_header();
\r
1388 $this->payload.='<methodName>' . $this->methodname . "</methodName>\n";
\r
1389 // if (sizeof($this->params)) {
\r
1390 $this->payload.="<params>\n";
\r
1391 for($i=0; $i<sizeof($this->params); $i++)
\r
1393 $p=$this->params[$i];
\r
1394 $this->payload.="<param>\n" . $p->serialize() .
\r
1397 $this->payload.="</params>\n";
\r
1399 $this->payload.=$this->xml_footer();
\r
1400 //$this->payload=str_replace("\n", "\r\n", $this->payload);
\r
1403 function method($meth='')
\r
1407 $this->methodname=$meth;
\r
1409 return $this->methodname;
\r
1412 function serialize()
\r
1414 $this->createPayload();
\r
1415 return $this->payload;
\r
1418 function addParam($par) { $this->params[]=$par; }
\r
1419 function getParam($i) { return $this->params[$i]; }
\r
1420 function getNumParams() { return sizeof($this->params); }
\r
1422 function parseResponseFile($fp)
\r
1425 while($data=fread($fp, 32768))
\r
1429 return $this->parseResponse($ipd);
\r
1432 function parseResponse($data='')
\r
1434 global $_xh,$xmlrpcerr,$xmlrpcstr;
\r
1435 global $xmlrpc_defencoding, $xmlrpc_internalencoding;
\r
1440 //by maHo, replaced htmlspecialchars with htmlentities
\r
1441 print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
\r
1446 error_log('No response received from server.');
\r
1447 $r = new xmlrpcresp(0, $xmlrpcerr['no_data'], $xmlrpcstr['no_data']);
\r
1450 // see if we got an HTTP 200 OK, else bomb
\r
1451 // but only do this if we're using the HTTP protocol.
\r
1452 if(ereg("^HTTP",$data))
\r
1454 // Strip HTTP 1.1 100 Continue header if present
\r
1455 while (ereg('^HTTP/1.1 1[0-9]{2}', $data))
\r
1457 $pos = strpos($data, 'HTTP', 12);
\r
1458 // server sent a Continue header without any (valid) content following...
\r
1459 // give the client a chance to know it
\r
1460 if (!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
\r
1462 $data = substr($data, $pos);
\r
1464 if (!ereg("^HTTP/[0-9\\.]+ 200 ", $data))
\r
1466 $errstr= substr($data, 0, strpos($data, "\n")-1);
\r
1467 error_log('HTTP error, got response: ' .$errstr);
\r
1468 $r=new xmlrpcresp(0, $xmlrpcerr['http_error'], $xmlrpcstr['http_error']. ' (' . $errstr . ')');
\r
1472 $parser = xml_parser_create($xmlrpc_defencoding);
\r
1474 // G. Giunta 2004/04/06
\r
1475 // Clean up the accumulator, or it will grow indefinitely long
\r
1476 // if making xmlrpc calls for a while
\r
1478 $_xh[$parser]=array();
\r
1479 $_xh[$parser]['headers'] = array();
\r
1480 $_xh[$parser]['stack'] = array();
\r
1481 $_xh[$parser]['valuestack'] = array();
\r
1483 // separate HTTP headers from data
\r
1484 if (ereg("^HTTP", $data))
\r
1486 // be tolerant to usage of \n instead of \r\n to separate headers and data
\r
1487 // (even though it is not valid http)
\r
1488 $pos = strpos($data,"\r\n\r\n");
\r
1489 if($pos || is_int($pos))
\r
1493 $pos = strpos($data,"\n\n");
\r
1494 if($pos || is_int($pos))
\r
1498 // No separation between response headers and body: fault?
\r
1502 // be tolerant to line endings, and extra empty lines
\r
1503 $ar = split("\r?\n", trim(substr($data, 0, $pos)));
\r
1504 while (list(,$line) = @each($ar))
\r
1506 // take care of multi-line headers
\r
1507 $arr = explode(':',$line);
\r
1508 if(count($arr) > 1)
\r
1510 $header_name = trim($arr[0]);
\r
1511 // TO DO: some headers (the ones that allow a CSV list of values)
\r
1512 // do allow many values to be passed using multiple header lines.
\r
1513 // We should add content to $_xh[$parser]['headers'][$header_name]
\r
1514 // instead of replacing it for those...
\r
1515 $_xh[$parser]['headers'][$header_name] = $arr[1];
\r
1516 for ($i = 2; $i < count($arr); $i++)
\r
1518 $_xh[$parser]['headers'][$header_name] .= ':'.$arr[$i];
\r
1520 $_xh[$parser]['headers'][$header_name] = trim($_xh[$parser]['headers'][$header_name]);
\r
1521 } else if (isset($header_name))
\r
1523 $_xh[$parser]['headers'][$header_name] .= ' ' . trim($line);
\r
1526 $data = substr($data, $bd);
\r
1528 if ($this->debug && count($_xh[$parser]['headers']))
\r
1531 //foreach ($_xh[$parser]['headers'] as $header)
\r
1532 @reset($_xh[$parser]['headers']);
\r
1533 while(list($header, $value) = @each($_xh[$parser]['headers']))
\r
1535 print "HEADER: $header: $value\n";
\r
1541 // be tolerant of extra whitespace in response body
\r
1542 $data = trim($data);
\r
1544 // be tolerant of junk after methodResponse (e.g. javascript automatically inserted by free hosts)
\r
1545 // idea from Luca Mariano <luca.mariano@email.it> originally in PEARified version of the lib
\r
1547 $pos = strpos($data, "</methodResponse>");
\r
1548 while ($pos || is_int($pos))
\r
1551 $pos = strpos($data, "</methodResponse>", $bd);
\r
1554 $data = substr($data, 0, $bd);
\r
1556 //$_xh[$parser]['st']='';
\r
1557 //$_xh[$parser]['cm']=0;
\r
1558 $_xh[$parser]['isf']=0;
\r
1559 $_xh[$parser]['isf_reason']=0;
\r
1560 $_xh[$parser]['ac']='';
\r
1561 //$_xh[$parser]['qt']='';
\r
1563 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
\r
1564 // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
\r
1565 // the xml parser to give us back data in the expected charset
\r
1566 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $xmlrpc_internalencoding);
\r
1568 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
\r
1569 xml_set_character_data_handler($parser, 'xmlrpc_cd');
\r
1570 xml_set_default_handler($parser, 'xmlrpc_dh');
\r
1571 //$xmlrpc_value=new xmlrpcval;
\r
1573 if (!xml_parse($parser, $data, sizeof($data)))
\r
1575 // thanks to Peter Kocks <peter.kocks@baygate.com>
\r
1576 if((xml_get_current_line_number($parser)) == 1)
\r
1578 $errstr = 'XML error at line 1, check URL';
\r
1582 $errstr = sprintf('XML error: %s at line %d',
\r
1583 xml_error_string(xml_get_error_code($parser)),
\r
1584 xml_get_current_line_number($parser));
\r
1586 error_log($errstr);
\r
1587 $r=new xmlrpcresp(0, $xmlrpcerr['invalid_return'], $xmlrpcstr['invalid_return'].' ('.$errstr.')');
\r
1588 xml_parser_free($parser);
\r
1591 $r->hdrs = $_xh[$parser]['headers'];
\r
1594 xml_parser_free($parser);
\r
1596 if ($_xh[$parser]['isf'] > 1)
\r
1600 ///@todo echo something for user?
\r
1603 $r = new xmlrpcresp(0, $xmlrpcerr['invalid_return'],
\r
1604 $xmlrpcstr['invalid_return'] . ' ' . $_xh[$parser]['isf_reason']);
\r
1606 //else if (strlen($_xh[$parser]['st'])==0)
\r
1607 else if (!is_object($_xh[$parser]['value']))
\r
1609 // then something odd has happened
\r
1610 // and it's time to generate a client side error
\r
1611 // indicating something odd went on
\r
1612 $r=new xmlrpcresp(0, $xmlrpcerr['invalid_return'],
\r
1613 $xmlrpcstr['invalid_return']);
\r
1620 //print "<PRE>---EVALING---[" .
\r
1621 //strlen($_xh[$parser]['st']) . " chars]---\n" .
\r
1622 //htmlspecialchars($_xh[$parser]['st']) . ";\n---END---</PRE>";
\r
1623 print "<PRE>---PARSED---\n" ;
\r
1624 var_dump($_xh[$parser]['value']);
\r
1625 print "\n---END---</PRE>";
\r
1629 //@eval('$v=' . $_xh[$parser]['st'] . '; $allOK=1;');
\r
1632 // $r = new xmlrpcresp(0, $xmlrpcerr['invalid_return'], $xmlrpcstr['invalid_return']);
\r
1635 $v = $_xh[$parser]['value'];
\r
1636 if ($_xh[$parser]['isf'])
\r
1638 $errno_v = $v->structmem('faultCode');
\r
1639 $errstr_v = $v->structmem('faultString');
\r
1640 $errno = $errno_v->scalarval();
\r
1644 // FAULT returned, errno needs to reflect that
\r
1648 $r = new xmlrpcresp($v, $errno, $errstr_v->scalarval());
\r
1652 $r=new xmlrpcresp($v);
\r
1656 $r->hdrs = $_xh[$parser]['headers'];
\r
1666 function xmlrpcval($val=-1, $type='')
\r
1668 global $xmlrpcTypes;
\r
1669 $this->me=array();
\r
1671 if ($val!=-1 || !is_int($val) || $type!='')
\r
1677 if ($xmlrpcTypes[$type]==1)
\r
1679 $this->addScalar($val,$type);
\r
1681 elseif ($xmlrpcTypes[$type]==2)
\r
1683 $this->addArray($val);
\r
1685 elseif ($xmlrpcTypes[$type]==3)
\r
1687 $this->addStruct($val);
\r
1692 function addScalar($val, $type='string')
\r
1694 global $xmlrpcTypes, $xmlrpcBoolean;
\r
1696 if ($this->mytype==1)
\r
1698 echo '<B>xmlrpcval</B>: scalar can have only one value<BR>';
\r
1701 $typeof=$xmlrpcTypes[$type];
\r
1704 echo '<B>xmlrpcval</B>: not a scalar type (${typeof})<BR>';
\r
1708 if ($type==$xmlrpcBoolean)
\r
1710 if (strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
\r
1720 if ($this->mytype==2)
\r
1722 // we're adding to an array here
\r
1723 $ar=$this->me['array'];
\r
1724 $ar[]=new xmlrpcval($val, $type);
\r
1725 $this->me['array']=$ar;
\r
1729 // a scalar, so set the value and remember we're scalar
\r
1730 $this->me[$type]=$val;
\r
1731 $this->mytype=$typeof;
\r
1736 function addArray($vals)
\r
1738 global $xmlrpcTypes;
\r
1739 if ($this->mytype!=0)
\r
1741 echo '<B>xmlrpcval</B>: already initialized as a [' . $this->kindOf() . ']<BR>';
\r
1745 $this->mytype=$xmlrpcTypes['array'];
\r
1746 $this->me['array']=$vals;
\r
1750 function addStruct($vals)
\r
1752 global $xmlrpcTypes;
\r
1753 if ($this->mytype!=0)
\r
1755 echo '<B>xmlrpcval</B>: already initialized as a [' . $this->kindOf() . ']<BR>';
\r
1758 $this->mytype=$xmlrpcTypes['struct'];
\r
1759 $this->me['struct']=$vals;
\r
1763 function dump($ar)
\r
1766 while ( list( $key, $val ) = each( $ar ) )
\r
1768 echo "$key => $val<br>";
\r
1769 if ($key == 'array')
\r
1771 while ( list( $key2, $val2 ) = each( $val ) )
\r
1773 echo "-- $key2 => $val2<br>";
\r
1781 switch($this->mytype)
\r
1797 function serializedata($typ, $val)
\r
1800 global $xmlrpcTypes, $xmlrpcBase64, $xmlrpcString,
\r
1802 switch(@$xmlrpcTypes[$typ])
\r
1806 $rs.="<struct>\n";
\r
1808 while(list($key2, $val2)=each($val))
\r
1810 $rs.="<member><name>${key2}</name>\n";
\r
1811 $rs.=$this->serializeval($val2);
\r
1812 $rs.="</member>\n";
\r
1818 $rs.="<array>\n<data>\n";
\r
1819 for($i=0; $i<sizeof($val); $i++)
\r
1821 $rs.=$this->serializeval($val[$i]);
\r
1823 $rs.="</data>\n</array>";
\r
1828 case $xmlrpcBase64:
\r
1829 $rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
\r
1831 case $xmlrpcBoolean:
\r
1832 $rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
\r
1834 case $xmlrpcString:
\r
1835 // G. Giunta 2005/2/13: do NOT use htmlentities, since
\r
1836 // it will produce named html entities, which are invalid xml
\r
1837 // $rs.="<${typ}>" . xmlrpc_encode_entitites($val). "</${typ}>";
\r
1838 // $rs.="<${typ}>" . htmlentities($val). "</${typ}>";
\r
1840 // N. Leenheer 2005/6/30: Use CDATA instead...
\r
1841 $rs.="<${typ}><![CDATA[" . $val. "]]></${typ}>";
\r
1844 $rs.="<${typ}>${val}</${typ}>";
\r
1853 function serialize()
\r
1855 return $this->serializeval($this);
\r
1858 function serializeval($o)
\r
1860 //global $xmlrpcTypes;
\r
1864 list($typ, $val) = each($ar);
\r
1866 $rs.=$this->serializedata($typ, $val);
\r
1867 $rs.="</value>\n";
\r
1871 function structmem($m)
\r
1873 $nv=$this->me['struct'][$m];
\r
1877 function structreset()
\r
1879 reset($this->me['struct']);
\r
1882 function structeach()
\r
1884 return each($this->me['struct']);
\r
1890 global $xmlrpcBoolean, $xmlrpcBase64;
\r
1892 list($a,$b)=each($this->me);
\r
1893 // contributed by I Sofer, 2001-03-24
\r
1894 // add support for nested arrays to scalarval
\r
1895 // i've created a new method here, so as to
\r
1896 // preserve back compatibility
\r
1901 while(list($id,$cont) = @each($b))
\r
1903 $b[$id] = $cont->scalarval();
\r
1907 // add support for structures directly encoding php objects
\r
1908 if (is_object($b))
\r
1910 $t = get_object_vars($b);
\r
1912 while(list($id,$cont) = @each($t))
\r
1914 $t[$id] = $cont->scalarval();
\r
1917 while(list($id,$cont) = @each($t))
\r
1919 //eval('$b->'.$id.' = $cont;');
\r
1927 function scalarval()
\r
1929 //global $xmlrpcBoolean, $xmlrpcBase64;
\r
1931 list($a,$b)=each($this->me);
\r
1935 function scalartyp()
\r
1937 global $xmlrpcI4, $xmlrpcInt;
\r
1939 list($a,$b)=each($this->me);
\r
1940 if ($a==$xmlrpcI4)
\r
1947 function arraymem($m)
\r
1949 $nv=$this->me['array'][$m];
\r
1953 function arraysize()
\r
1956 list($a,$b)=each($this->me);
\r
1957 return sizeof($b);
\r
1962 function iso8601_encode($timet, $utc=0)
\r
1964 // return an ISO8601 encoded string
\r
1965 // really, timezones ought to be supported
\r
1966 // but the XML-RPC spec says:
\r
1968 // "Don't assume a timezone. It should be specified by the server in its
\r
1969 // documentation what assumptions it makes about timezones."
\r
1971 // these routines always assume localtime unless
\r
1972 // $utc is set to 1, in which case UTC is assumed
\r
1973 // and an adjustment for locale is made when encoding
\r
1976 $t=strftime("%Y%m%dT%H:%M:%S", $timet);
\r
1980 if (function_exists('gmstrftime'))
\r
1982 // gmstrftime doesn't exist in some versions
\r
1984 $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
\r
1988 $t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z'));
\r
1994 function iso8601_decode($idate, $utc=0)
\r
1996 // return a timet in the localtime, or UTC
\r
1998 if (ereg("([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})", $idate, $regs))
\r
2002 $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
\r
2006 $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
\r
2012 /****************************************************************
\r
2013 * xmlrpc_decode takes a message in PHP xmlrpc object format and *
\r
2014 * tranlates it into native PHP types. *
\r
2016 * author: Dan Libby (dan@libby.com) *
\r
2017 ****************************************************************/
\r
2018 function php_xmlrpc_decode($xmlrpc_val)
\r
2020 $kind = $xmlrpc_val->kindOf();
\r
2022 if($kind == 'scalar')
\r
2024 return $xmlrpc_val->scalarval();
\r
2026 elseif($kind == 'array')
\r
2028 $size = $xmlrpc_val->arraysize();
\r
2031 for($i = 0; $i < $size; $i++)
\r
2033 $arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i));
\r
2037 elseif($kind == 'struct')
\r
2039 $xmlrpc_val->structreset();
\r
2042 while(list($key,$value)=$xmlrpc_val->structeach())
\r
2044 $arr[$key] = php_xmlrpc_decode($value);
\r
2050 if(function_exists('xmlrpc_decode'))
\r
2052 define('XMLRPC_EPI_ENABLED','1');
\r
2056 define('XMLRPC_EPI_ENABLED','0');
\r
2057 function xmlrpc_decode($xmlrpc_val)
\r
2059 $kind = $xmlrpc_val->kindOf();
\r
2061 if($kind == 'scalar')
\r
2063 return $xmlrpc_val->scalarval();
\r
2065 elseif($kind == 'array')
\r
2067 $size = $xmlrpc_val->arraysize();
\r
2070 for($i = 0; $i < $size; $i++)
\r
2072 $arr[]=xmlrpc_decode($xmlrpc_val->arraymem($i));
\r
2076 elseif($kind == 'struct')
\r
2078 $xmlrpc_val->structreset();
\r
2081 while(list($key,$value)=$xmlrpc_val->structeach())
\r
2083 $arr[$key] = xmlrpc_decode($value);
\r
2090 /****************************************************************
\r
2091 * xmlrpc_encode takes native php types and encodes them into *
\r
2092 * xmlrpc PHP object format. *
\r
2093 * BUG: All sequential arrays are turned into structs. I don't *
\r
2094 * know of a good way to determine if an array is sequential *
\r
2097 * feature creep -- could support more types via optional type *
\r
2100 * author: Dan Libby (dan@libby.com) *
\r
2101 ****************************************************************/
\r
2102 function php_xmlrpc_encode($php_val)
\r
2104 global $xmlrpcInt;
\r
2105 global $xmlrpcDouble;
\r
2106 global $xmlrpcString;
\r
2107 global $xmlrpcArray;
\r
2108 global $xmlrpcStruct;
\r
2109 global $xmlrpcBoolean;
\r
2111 $type = gettype($php_val);
\r
2112 $xmlrpc_val = new xmlrpcval;
\r
2119 while (list($k,$v) = each($php_val))
\r
2121 $arr[$k] = php_xmlrpc_encode($v);
\r
2123 $xmlrpc_val->addStruct($arr);
\r
2126 $xmlrpc_val->addScalar($php_val, $xmlrpcInt);
\r
2129 $xmlrpc_val->addScalar($php_val, $xmlrpcDouble);
\r
2132 $xmlrpc_val->addScalar($php_val, $xmlrpcString);
\r
2134 // <G_Giunta_2001-02-29>
\r
2135 // Add support for encoding/decoding of booleans, since they are supported in PHP
\r
2137 $xmlrpc_val->addScalar($php_val, $xmlrpcBoolean);
\r
2139 // </G_Giunta_2001-02-29>
\r
2140 // catch "resource", "NULL", "user function", "unknown type"
\r
2141 //case 'unknown type':
\r
2143 // giancarlo pinerolo <ping@alt.it>
\r
2144 // it has to return
\r
2145 // an empty object in case (which is already
\r
2146 // at this point), not a boolean.
\r
2149 return $xmlrpc_val;
\r
2152 if(XMLRPC_EPI_ENABLED == '0')
\r
2154 function xmlrpc_encode($php_val)
\r
2156 global $xmlrpcInt;
\r
2157 global $xmlrpcDouble;
\r
2158 global $xmlrpcString;
\r
2159 global $xmlrpcArray;
\r
2160 global $xmlrpcStruct;
\r
2161 global $xmlrpcBoolean;
\r
2163 $type = gettype($php_val);
\r
2164 $xmlrpc_val = new xmlrpcval;
\r
2171 while (list($k,$v) = each($php_val))
\r
2173 $arr[$k] = xmlrpc_encode($v);
\r
2175 $xmlrpc_val->addStruct($arr);
\r
2178 $xmlrpc_val->addScalar($php_val, $xmlrpcInt);
\r
2181 $xmlrpc_val->addScalar($php_val, $xmlrpcDouble);
\r
2184 $xmlrpc_val->addScalar($php_val, $xmlrpcString);
\r
2186 // <G_Giunta_2001-02-29>
\r
2187 // Add support for encoding/decoding of booleans, since they are supported in PHP
\r
2189 $xmlrpc_val->addScalar($php_val, $xmlrpcBoolean);
\r
2191 // </G_Giunta_2001-02-29>
\r
2192 //case 'unknown type':
\r
2194 // giancarlo pinerolo <ping@alt.it>
\r
2195 // it has to return
\r
2196 // an empty object in case (which is already
\r
2197 // at this point), not a boolean.
\r
2200 return $xmlrpc_val;
\r