travis tests
[plcapi.git] / demo / server / server.php
1 <?php
2 /**
3  * Demo server for xmlrpc library.
4  *
5  * Implements a lot of webservices, including a suite of services used for
6  * interoperability testing (validator1 methods), and some whose only purpose
7  * is to be used for unit-testing the library.
8  *
9  * Please do not copy this file verbatim into your production server.
10  **/
11
12 require_once __DIR__ . "/_bootstrap.php";
13
14 // Out-of-band information: let the client manipulate the server operations.
15 // We do this to help the testsuite script: do not reproduce in production!
16 if (isset($_COOKIE['PHPUNIT_SELENIUM_TEST_ID']) && extension_loaded('xdebug')) {
17     $GLOBALS['PHPUNIT_COVERAGE_DATA_DIRECTORY'] = '/tmp/phpxmlrpc_coverage';
18     if (!is_dir($GLOBALS['PHPUNIT_COVERAGE_DATA_DIRECTORY'])) {
19         mkdir($GLOBALS['PHPUNIT_COVERAGE_DATA_DIRECTORY']);
20     }
21
22     include_once __DIR__ . "/../../vendor/phpunit/phpunit-selenium/PHPUnit/Extensions/SeleniumCommon/prepend.php";
23 }
24
25 use PhpXmlRpc\Value;
26
27 /**
28  * Used to test usage of object methods in dispatch maps and in wrapper code.
29  */
30 class xmlrpcServerMethodsContainer
31 {
32     /**
33      * Method used to test logging of php warnings generated by user functions.
34      * @param PhpXmlRpc\Request $req
35      * @return PhpXmlRpc\Response
36      */
37     public function phpWarningGenerator($req)
38     {
39         $a = $undefinedVariable; // this triggers a warning in E_ALL mode, since $undefinedVariable is undefined
40         return new PhpXmlRpc\Response(new Value(1, Value::$xmlrpcBoolean));
41     }
42
43     /**
44      * Method used to test catching of exceptions in the server.
45      * @param PhpXmlRpc\Request $req
46      * @throws Exception
47      */
48     public function exceptionGenerator($req)
49     {
50         throw new Exception("it's just a test", 1);
51     }
52
53     /**
54      * @param string $msg
55      */
56     public function debugMessageGenerator($msg)
57     {
58         PhpXmlRpc\Server::xmlrpc_debugmsg($msg);
59     }
60
61     /**
62      * A PHP version of the state-number server. Send me an integer and i'll sell you a state.
63      * Used to test wrapping of PHP methods into xmlrpc methods.
64      *
65      * @param integer $num
66      * @return string
67      * @throws Exception
68      */
69     public static function findState($num)
70     {
71         return inner_findstate($num);
72     }
73
74     /**
75      * Returns an instance of stdClass.
76      * Used to test wrapping of PHP objects with class preservation
77      */
78     public function returnObject()
79     {
80         $obj = new stdClass();
81         $obj->hello = 'world';
82         return $obj;
83     }
84 }
85
86 // a PHP version of the state-number server
87 // send me an integer and i'll sell you a state
88
89 $stateNames = array(
90     "Alabama", "Alaska", "Arizona", "Arkansas", "California",
91     "Colorado", "Columbia", "Connecticut", "Delaware", "Florida",
92     "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas",
93     "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan",
94     "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada",
95     "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina",
96     "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island",
97     "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont",
98     "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming",
99 );
100
101 $findstate_sig = array(array(Value::$xmlrpcString, Value::$xmlrpcInt));
102 $findstate_doc = 'When passed an integer between 1 and 51 returns the
103 name of a US state, where the integer is the index of that state name
104 in an alphabetic order.';
105
106 function findState($req)
107 {
108     global $stateNames;
109
110     $err = "";
111     // get the first param
112     $sno = $req->getParam(0);
113
114     // param must be there and of the correct type: server object does the validation for us
115
116     // extract the value of the state number
117     $snv = $sno->scalarval();
118     // look it up in our array (zero-based)
119     if (isset($stateNames[$snv - 1])) {
120         $stateName = $stateNames[$snv - 1];
121     } else {
122         // not there, so complain
123         $err = "I don't have a state for the index '" . $snv . "'";
124     }
125
126     // if we generated an error, create an error return response
127     if ($err) {
128         return new PhpXmlRpc\Response(0, PhpXmlRpc\PhpXmlRpc::$xmlrpcerruser, $err);
129     } else {
130         // otherwise, we create the right response with the state name
131         return new PhpXmlRpc\Response(new Value($stateName));
132     }
133 }
134
135 /**
136  * Inner code of the state-number server.
137  * Used to test wrapping of PHP functions into xmlrpc methods.
138  *
139  * @param integer $stateNo the state number
140  *
141  * @return string the name of the state (or error description)
142  *
143  * @throws Exception if state is not found
144  */
145 function inner_findstate($stateNo)
146 {
147     global $stateNames;
148
149     if (isset($stateNames[$stateNo - 1])) {
150         return $stateNames[$stateNo - 1];
151     } else {
152         // not, there so complain
153         throw new Exception("I don't have a state for the index '" . $stateNo . "'", PhpXmlRpc\PhpXmlRpc::$xmlrpcerruser);
154     }
155 }
156
157 $wrapper = new PhpXmlRpc\Wrapper();
158
159 $findstate2_sig = $wrapper->wrapPhpFunction('inner_findstate');
160
161 $findstate3_sig = $wrapper->wrapPhpFunction(array('xmlrpcServerMethodsContainer', 'findState'));
162
163 $obj = new xmlrpcServerMethodsContainer();
164 $findstate4_sig = $wrapper->wrapPhpFunction(array($obj, 'findstate'));
165
166 $findstate5_sig = $wrapper->wrapPhpFunction('xmlrpcServerMethodsContainer::findState', '', array('return_source' => true));
167 eval($findstate5_sig['source']);
168
169 $findstate6_sig = $wrapper->wrapPhpFunction('inner_findstate', '', array('return_source' => true));
170 eval($findstate6_sig['source']);
171
172 $findstate7_sig = $wrapper->wrapPhpFunction(array('xmlrpcServerMethodsContainer', 'findState'), '', array('return_source' => true));
173 eval($findstate7_sig['source']);
174
175 $obj = new xmlrpcServerMethodsContainer();
176 $findstate8_sig = $wrapper->wrapPhpFunction(array($obj, 'findstate'), '', array('return_source' => true));
177 eval($findstate8_sig['source']);
178
179 $findstate9_sig = $wrapper->wrapPhpFunction('xmlrpcServerMethodsContainer::findState', '', array('return_source' => true));
180 eval($findstate9_sig['source']);
181
182 $findstate10_sig = array(
183     "function" => function ($req) { return findState($req); },
184     "signature" => $findstate_sig,
185     "docstring" => $findstate_doc,
186 );
187
188 $findstate11_sig = $wrapper->wrapPhpFunction(function ($stateNo) { return inner_findstate($stateNo); });
189
190 $c = new xmlrpcServerMethodsContainer;
191 $moreSignatures = $wrapper->wrapPhpClass($c, array('prefix' => 'tests.', 'method_type' => 'all'));
192
193 $returnObj_sig =  $wrapper->wrapPhpFunction(array($c, 'returnObject'), '', array('encode_php_objs' => true));
194
195 // used to test signatures with NULL params
196 $findstate12_sig = array(
197     array(Value::$xmlrpcString, Value::$xmlrpcInt, Value::$xmlrpcNull),
198     array(Value::$xmlrpcString, Value::$xmlrpcNull, Value::$xmlrpcInt),
199 );
200
201 function findStateWithNulls($req)
202 {
203     $a = $req->getParam(0);
204     $b = $req->getParam(1);
205
206     if ($a->scalartyp() == Value::$xmlrpcNull)
207         return new PhpXmlRpc\Response(new Value(inner_findstate($b->scalarval())));
208     else
209         return new PhpXmlRpc\Response(new Value(inner_findstate($a->scalarval())));
210 }
211
212 $addtwo_sig = array(array(Value::$xmlrpcInt, Value::$xmlrpcInt, Value::$xmlrpcInt));
213 $addtwo_doc = 'Add two integers together and return the result';
214 function addTwo($req)
215 {
216     $s = $req->getParam(0);
217     $t = $req->getParam(1);
218
219     return new PhpXmlRpc\Response(new Value($s->scalarval() + $t->scalarval(), Value::$xmlrpcInt));
220 }
221
222 $addtwodouble_sig = array(array(Value::$xmlrpcDouble, Value::$xmlrpcDouble, Value::$xmlrpcDouble));
223 $addtwodouble_doc = 'Add two doubles together and return the result';
224 function addTwoDouble($req)
225 {
226     $s = $req->getParam(0);
227     $t = $req->getParam(1);
228
229     return new PhpXmlRpc\Response(new Value($s->scalarval() + $t->scalarval(), Value::$xmlrpcDouble));
230 }
231
232 $stringecho_sig = array(array(Value::$xmlrpcString, Value::$xmlrpcString));
233 $stringecho_doc = 'Accepts a string parameter, returns the string.';
234 function stringEcho($req)
235 {
236     // just sends back a string
237     return new PhpXmlRpc\Response(new Value($req->getParam(0)->scalarval()));
238 }
239
240 $echoback_sig = array(array(Value::$xmlrpcString, Value::$xmlrpcString));
241 $echoback_doc = 'Accepts a string parameter, returns the entire incoming payload';
242 function echoBack($req)
243 {
244     // just sends back a string with what i got sent to me, just escaped, that's all
245     $s = "I got the following message:\n" . $req->serialize();
246
247     return new PhpXmlRpc\Response(new Value($s));
248 }
249
250 $echosixtyfour_sig = array(array(Value::$xmlrpcString, Value::$xmlrpcBase64));
251 $echosixtyfour_doc = 'Accepts a base64 parameter and returns it decoded as a string';
252 function echoSixtyFour($req)
253 {
254     // Accepts an encoded value, but sends it back as a normal string.
255     // This is to test that base64 encoding is working as expected
256     $incoming = $req->getParam(0);
257
258     return new PhpXmlRpc\Response(new Value($incoming->scalarval(), Value::$xmlrpcString));
259 }
260
261 $bitflipper_sig = array(array(Value::$xmlrpcArray, Value::$xmlrpcArray));
262 $bitflipper_doc = 'Accepts an array of booleans, and returns them inverted';
263 function bitFlipper($req)
264 {
265     $v = $req->getParam(0);
266     $rv = new Value(array(), Value::$xmlrpcArray);
267
268     foreach ($v as $b) {
269         if ($b->scalarval()) {
270             $rv[] = new Value(false, Value::$xmlrpcBoolean);
271         } else {
272             $rv[] = new Value(true, Value::$xmlrpcBoolean);
273         }
274     }
275
276     return new PhpXmlRpc\Response($rv);
277 }
278
279 // Sorting demo
280 //
281 // send me an array of structs thus:
282 //
283 // Dave 35
284 // Edd  45
285 // Fred 23
286 // Barney 37
287 //
288 // and I'll return it to you in sorted order
289
290 function agesorter_compare($a, $b)
291 {
292     global $agesorter_arr;
293
294     // don't even ask me _why_ these come padded with hyphens, I couldn't tell you :p
295     $a = str_replace("-", "", $a);
296     $b = str_replace("-", "", $b);
297
298     if ($agesorter_arr[$a] == $agesorter_arr[$b]) {
299         return 0;
300     }
301
302     return ($agesorter_arr[$a] > $agesorter_arr[$b]) ? -1 : 1;
303 }
304
305 $agesorter_sig = array(array(Value::$xmlrpcArray, Value::$xmlrpcArray));
306 $agesorter_doc = 'Send this method an array of [string, int] structs, eg:
307 <pre>
308  Dave   35
309  Edd    45
310  Fred   23
311  Barney 37
312 </pre>
313 And the array will be returned with the entries sorted by their numbers.
314 ';
315 function ageSorter($req)
316 {
317     global $agesorter_arr, $s;
318
319     PhpXmlRpc\Server::xmlrpc_debugmsg("Entering 'agesorter'");
320     // get the parameter
321     $sno = $req->getParam(0);
322     // error string for [if|when] things go wrong
323     $err = "";
324     $agar = array();
325
326     $max = $sno->count();
327     PhpXmlRpc\Server::xmlrpc_debugmsg("Found $max array elements");
328     foreach ($sno as $i => $rec) {
329         if ($rec->kindOf() != "struct") {
330             $err = "Found non-struct in array at element $i";
331             break;
332         }
333         // extract name and age from struct
334         $n = $rec["name"];
335         $a = $rec["age"];
336         // $n and $a are xmlrpcvals,
337         // so get the scalarval from them
338         $agar[$n->scalarval()] = $a->scalarval();
339     }
340
341     // create the output value
342     $v = new Value(array(), Value::$xmlrpcArray);
343
344     $agesorter_arr = $agar;
345     // hack, must make global as uksort() won't
346     // allow us to pass any other auxiliary information
347     uksort($agesorter_arr, 'agesorter_compare');
348     foreach($agesorter_arr as $key => $val) {
349         // recreate each struct element
350         $v[] = new Value(
351             array(
352                 "name" => new Value($key),
353                 "age" => new Value($val, "int")
354             ),
355             Value::$xmlrpcStruct
356         );
357     }
358
359     if ($err) {
360         return new PhpXmlRpc\Response(0, PhpXmlRpc\PhpXmlRpc::$xmlrpcerruser, $err);
361     } else {
362         return new PhpXmlRpc\Response($v);
363     }
364 }
365
366 // signature and instructions, place these in the dispatch map
367 $mailsend_sig = array(array(
368     Value::$xmlrpcBoolean, Value::$xmlrpcString, Value::$xmlrpcString,
369     Value::$xmlrpcString, Value::$xmlrpcString, Value::$xmlrpcString,
370     Value::$xmlrpcString, Value::$xmlrpcString,
371 ));
372 $mailsend_doc = 'mail.send(recipient, subject, text, sender, cc, bcc, mimetype)<br/>
373 recipient, cc, and bcc are strings, comma-separated lists of email addresses, as described above.<br/>
374 subject is a string, the subject of the message.<br/>
375 sender is a string, it\'s the email address of the person sending the message. This string can not be
376 a comma-separated list, it must contain a single email address only.<br/>
377 text is a string, it contains the body of the message.<br/>
378 mimetype, a string, is a standard MIME type, for example, text/plain.
379 ';
380 // WARNING; this functionality depends on the sendmail -t option
381 // it may not work with Windows machines properly; particularly
382 // the Bcc option. Sneak on your friends at your own risk!
383 function mailSend($req)
384 {
385     $err = "";
386
387     $mTo = $req->getParam(0);
388     $mSub = $req->getParam(1);
389     $mBody = $req->getParam(2);
390     $mFrom = $req->getParam(3);
391     $mCc = $req->getParam(4);
392     $mBcc = $req->getParam(5);
393     $mMime = $req->getParam(6);
394
395     if ($mTo->scalarval() == "") {
396         $err = "Error, no 'To' field specified";
397     }
398
399     if ($mFrom->scalarval() == "") {
400         $err = "Error, no 'From' field specified";
401     }
402
403     $msgHdr = "From: " . $mFrom->scalarval() . "\n";
404     $msgHdr .= "To: " . $mTo->scalarval() . "\n";
405
406     if ($mCc->scalarval() != "") {
407         $msgHdr .= "Cc: " . $mCc->scalarval() . "\n";
408     }
409     if ($mBcc->scalarval() != "") {
410         $msgHdr .= "Bcc: " . $mBcc->scalarval() . "\n";
411     }
412     if ($mMime->scalarval() != "") {
413         $msgHdr .= "Content-type: " . $mMime->scalarval() . "\n";
414     }
415     $msgHdr .= "X-Mailer: XML-RPC for PHP mailer 1.0";
416
417     if ($err == "") {
418         if (!mail("",
419             $mSub->scalarval(),
420             $mBody->scalarval(),
421             $msgHdr)
422         ) {
423             $err = "Error, could not send the mail.";
424         }
425     }
426
427     if ($err) {
428         return new PhpXmlRpc\Response(0, PhpXmlRpc\PhpXmlRpc::$xmlrpcerruser, $err);
429     } else {
430         return new PhpXmlRpc\Response(new Value(true, Value::$xmlrpcBoolean));
431     }
432 }
433
434 $getallheaders_sig = array(array(Value::$xmlrpcStruct));
435 $getallheaders_doc = 'Returns a struct containing all the HTTP headers received with the request. Provides limited functionality with IIS';
436 function getAllHeaders_xmlrpc($req)
437 {
438     $encoder = new PhpXmlRpc\Encoder();
439
440     if (function_exists('getallheaders')) {
441         return new PhpXmlRpc\Response($encoder->encode(getallheaders()));
442     } else {
443         $headers = array();
444         // IIS: poor man's version of getallheaders
445         foreach ($_SERVER as $key => $val) {
446             if (strpos($key, 'HTTP_') === 0) {
447                 $key = ucfirst(str_replace('_', '-', strtolower(substr($key, 5))));
448                 $headers[$key] = $val;
449             }
450         }
451
452         return new PhpXmlRpc\Response($encoder->encode($headers));
453     }
454 }
455
456 $setcookies_sig = array(array(Value::$xmlrpcInt, Value::$xmlrpcStruct));
457 $setcookies_doc = 'Sends to client a response containing a single \'1\' digit, and sets to it http cookies as received in the request (array of structs describing a cookie)';
458 function setCookies($req)
459 {
460     $encoder = new PhpXmlRpc\Encoder();
461     $cookies = $req->getParam(0);
462     foreach ($cookies as $name => $value) {
463         $cookieDesc = $encoder->decode($value);
464         setcookie($name, @$cookieDesc['value'], @$cookieDesc['expires'], @$cookieDesc['path'], @$cookieDesc['domain'], @$cookieDesc['secure']);
465     }
466
467     return new PhpXmlRpc\Response(new Value(1, Value::$xmlrpcInt));
468 }
469
470 $getcookies_sig = array(array(Value::$xmlrpcStruct));
471 $getcookies_doc = 'Sends to client a response containing all http cookies as received in the request (as struct)';
472 function getCookies($req)
473 {
474     $encoder = new PhpXmlRpc\Encoder();
475     return new PhpXmlRpc\Response($encoder->encode($_COOKIE));
476 }
477
478 $v1_arrayOfStructs_sig = array(array(Value::$xmlrpcInt, Value::$xmlrpcArray));
479 $v1_arrayOfStructs_doc = 'This handler takes a single parameter, an array of structs, each of which contains at least three elements named moe, larry and curly, all <i4>s. Your handler must add all the struct elements named curly and return the result.';
480 function v1_arrayOfStructs($req)
481 {
482     $sno = $req->getParam(0);
483     $numCurly = 0;
484     foreach ($sno as $str) {
485         foreach ($str as $key => $val) {
486             if ($key == "curly") {
487                 $numCurly += $val->scalarval();
488             }
489         }
490     }
491
492     return new PhpXmlRpc\Response(new Value($numCurly, Value::$xmlrpcInt));
493 }
494
495 $v1_easyStruct_sig = array(array(Value::$xmlrpcInt, Value::$xmlrpcStruct));
496 $v1_easyStruct_doc = 'This handler takes a single parameter, a struct, containing at least three elements named moe, larry and curly, all &lt;i4&gt;s. Your handler must add the three numbers and return the result.';
497 function v1_easyStruct($req)
498 {
499     $sno = $req->getParam(0);
500     $moe = $sno["moe"];
501     $larry = $sno["larry"];
502     $curly = $sno["curly"];
503     $num = $moe->scalarval() + $larry->scalarval() + $curly->scalarval();
504
505     return new PhpXmlRpc\Response(new Value($num, Value::$xmlrpcInt));
506 }
507
508 $v1_echoStruct_sig = array(array(Value::$xmlrpcStruct, Value::$xmlrpcStruct));
509 $v1_echoStruct_doc = 'This handler takes a single parameter, a struct. Your handler must return the struct.';
510 function v1_echoStruct($req)
511 {
512     $sno = $req->getParam(0);
513
514     return new PhpXmlRpc\Response($sno);
515 }
516
517 $v1_manyTypes_sig = array(array(
518     Value::$xmlrpcArray, Value::$xmlrpcInt, Value::$xmlrpcBoolean,
519     Value::$xmlrpcString, Value::$xmlrpcDouble, Value::$xmlrpcDateTime,
520     Value::$xmlrpcBase64,
521 ));
522 $v1_manyTypes_doc = 'This handler takes six parameters, and returns an array containing all the parameters.';
523 function v1_manyTypes($req)
524 {
525     return new PhpXmlRpc\Response(new Value(
526         array(
527             $req->getParam(0),
528             $req->getParam(1),
529             $req->getParam(2),
530             $req->getParam(3),
531             $req->getParam(4),
532             $req->getParam(5)
533         ),
534         Value::$xmlrpcArray
535     ));
536 }
537
538 $v1_moderateSizeArrayCheck_sig = array(array(Value::$xmlrpcString, Value::$xmlrpcArray));
539 $v1_moderateSizeArrayCheck_doc = 'This handler takes a single parameter, which is an array containing between 100 and 200 elements. Each of the items is a string, your handler must return a string containing the concatenated text of the first and last elements.';
540 function v1_moderateSizeArrayCheck($req)
541 {
542     $ar = $req->getParam(0);
543     $sz = $ar->count();
544     $first = $ar[0];
545     $last = $ar[$sz - 1];
546
547     return new PhpXmlRpc\Response(new Value($first->scalarval() .
548         $last->scalarval(), Value::$xmlrpcString));
549 }
550
551 $v1_simpleStructReturn_sig = array(array(Value::$xmlrpcStruct, Value::$xmlrpcInt));
552 $v1_simpleStructReturn_doc = 'This handler takes one parameter, and returns a struct containing three elements, times10, times100 and times1000, the result of multiplying the number by 10, 100 and 1000.';
553 function v1_simpleStructReturn($req)
554 {
555     $sno = $req->getParam(0);
556     $v = $sno->scalarval();
557
558     return new PhpXmlRpc\Response(new Value(
559         array(
560             "times10" => new Value($v * 10, Value::$xmlrpcInt),
561             "times100" => new Value($v * 100, Value::$xmlrpcInt),
562             "times1000" => new Value($v * 1000, Value::$xmlrpcInt)
563         ),
564         Value::$xmlrpcStruct
565     ));
566 }
567
568 $v1_nestedStruct_sig = array(array(Value::$xmlrpcInt, Value::$xmlrpcStruct));
569 $v1_nestedStruct_doc = 'This handler takes a single parameter, a struct, that models a daily calendar. At the top level, there is one struct for each year. Each year is broken down into months, and months into days. Most of the days are empty in the struct you receive, but the entry for April 1, 2000 contains a least three elements named moe, larry and curly, all &lt;i4&gt;s. Your handler must add the three numbers and return the result.';
570 function v1_nestedStruct($req)
571 {
572     $sno = $req->getParam(0);
573
574     $twoK = $sno["2000"];
575     $april = $twoK["04"];
576     $fools = $april["01"];
577     $curly = $fools["curly"];
578     $larry = $fools["larry"];
579     $moe = $fools["moe"];
580
581     return new PhpXmlRpc\Response(new Value($curly->scalarval() + $larry->scalarval() + $moe->scalarval(), Value::$xmlrpcInt));
582 }
583
584 $v1_countTheEntities_sig = array(array(Value::$xmlrpcStruct, Value::$xmlrpcString));
585 $v1_countTheEntities_doc = 'This handler takes a single parameter, a string, that contains any number of predefined entities, namely &lt;, &gt;, &amp; \' and ".<BR>Your handler must return a struct that contains five fields, all numbers: ctLeftAngleBrackets, ctRightAngleBrackets, ctAmpersands, ctApostrophes, ctQuotes.';
586 function v1_countTheEntities($req)
587 {
588     $sno = $req->getParam(0);
589     $str = $sno->scalarval();
590     $gt = 0;
591     $lt = 0;
592     $ap = 0;
593     $qu = 0;
594     $amp = 0;
595     for ($i = 0; $i < strlen($str); $i++) {
596         $c = substr($str, $i, 1);
597         switch ($c) {
598             case ">":
599                 $gt++;
600                 break;
601             case "<":
602                 $lt++;
603                 break;
604             case "\"":
605                 $qu++;
606                 break;
607             case "'":
608                 $ap++;
609                 break;
610             case "&":
611                 $amp++;
612                 break;
613             default:
614                 break;
615         }
616     }
617
618     return new PhpXmlRpc\Response(new Value(
619         array(
620             "ctLeftAngleBrackets" => new Value($lt, Value::$xmlrpcInt),
621             "ctRightAngleBrackets" => new Value($gt, Value::$xmlrpcInt),
622             "ctAmpersands" => new Value($amp, Value::$xmlrpcInt),
623             "ctApostrophes" => new Value($ap, Value::$xmlrpcInt),
624             "ctQuotes" => new Value($qu, Value::$xmlrpcInt)
625         ),
626         Value::$xmlrpcStruct
627     ));
628 }
629
630 // trivial interop tests
631 // http://www.xmlrpc.com/stories/storyReader$1636
632
633 $i_echoString_sig = array(array(Value::$xmlrpcString, Value::$xmlrpcString));
634 $i_echoString_doc = "Echoes string.";
635
636 $i_echoStringArray_sig = array(array(Value::$xmlrpcArray, Value::$xmlrpcArray));
637 $i_echoStringArray_doc = "Echoes string array.";
638
639 $i_echoInteger_sig = array(array(Value::$xmlrpcInt, Value::$xmlrpcInt));
640 $i_echoInteger_doc = "Echoes integer.";
641
642 $i_echoIntegerArray_sig = array(array(Value::$xmlrpcArray, Value::$xmlrpcArray));
643 $i_echoIntegerArray_doc = "Echoes integer array.";
644
645 $i_echoFloat_sig = array(array(Value::$xmlrpcDouble, Value::$xmlrpcDouble));
646 $i_echoFloat_doc = "Echoes float.";
647
648 $i_echoFloatArray_sig = array(array(Value::$xmlrpcArray, Value::$xmlrpcArray));
649 $i_echoFloatArray_doc = "Echoes float array.";
650
651 $i_echoStruct_sig = array(array(Value::$xmlrpcStruct, Value::$xmlrpcStruct));
652 $i_echoStruct_doc = "Echoes struct.";
653
654 $i_echoStructArray_sig = array(array(Value::$xmlrpcArray, Value::$xmlrpcArray));
655 $i_echoStructArray_doc = "Echoes struct array.";
656
657 $i_echoValue_doc = "Echoes any value back.";
658 $i_echoValue_sig = array(array(Value::$xmlrpcValue, Value::$xmlrpcValue));
659
660 $i_echoBase64_sig = array(array(Value::$xmlrpcBase64, Value::$xmlrpcBase64));
661 $i_echoBase64_doc = "Echoes base64.";
662
663 $i_echoDate_sig = array(array(Value::$xmlrpcDateTime, Value::$xmlrpcDateTime));
664 $i_echoDate_doc = "Echoes dateTime.";
665
666 function i_echoParam($req)
667 {
668     $s = $req->getParam(0);
669
670     return new PhpXmlRpc\Response($s);
671 }
672
673 function i_echoString($req)
674 {
675     return i_echoParam($req);
676 }
677
678 function i_echoInteger($req)
679 {
680     return i_echoParam($req);
681 }
682
683 function i_echoFloat($req)
684 {
685     return i_echoParam($req);
686 }
687
688 function i_echoStruct($req)
689 {
690     return i_echoParam($req);
691 }
692
693 function i_echoStringArray($req)
694 {
695     return i_echoParam($req);
696 }
697
698 function i_echoIntegerArray($req)
699 {
700     return i_echoParam($req);
701 }
702
703 function i_echoFloatArray($req)
704 {
705     return i_echoParam($req);
706 }
707
708 function i_echoStructArray($req)
709 {
710     return i_echoParam($req);
711 }
712
713 function i_echoValue($req)
714 {
715     return i_echoParam($req);
716 }
717
718 function i_echoBase64($req)
719 {
720     return i_echoParam($req);
721 }
722
723 function i_echoDate($req)
724 {
725     return i_echoParam($req);
726 }
727
728 $i_whichToolkit_sig = array(array(Value::$xmlrpcStruct));
729 $i_whichToolkit_doc = "Returns a struct containing the following strings: toolkitDocsUrl, toolkitName, toolkitVersion, toolkitOperatingSystem.";
730
731 function i_whichToolkit($req)
732 {
733     global $SERVER_SOFTWARE;
734     $ret = array(
735         "toolkitDocsUrl" => "http://phpxmlrpc.sourceforge.net/",
736         "toolkitName" => PhpXmlRpc\PhpXmlRpc::$xmlrpcName,
737         "toolkitVersion" => PhpXmlRpc\PhpXmlRpc::$xmlrpcVersion,
738         "toolkitOperatingSystem" => isset($SERVER_SOFTWARE) ? $SERVER_SOFTWARE : $_SERVER['SERVER_SOFTWARE'],
739     );
740
741     $encoder = new PhpXmlRpc\Encoder();
742     return new PhpXmlRpc\Response($encoder->encode($ret));
743 }
744
745 $object = new xmlrpcServerMethodsContainer();
746 $signatures = array(
747     "examples.getStateName" => array(
748         "function" => "findState",
749         "signature" => $findstate_sig,
750         "docstring" => $findstate_doc,
751     ),
752     "examples.sortByAge" => array(
753         "function" => "ageSorter",
754         "signature" => $agesorter_sig,
755         "docstring" => $agesorter_doc,
756     ),
757     "examples.addtwo" => array(
758         "function" => "addTwo",
759         "signature" => $addtwo_sig,
760         "docstring" => $addtwo_doc,
761     ),
762     "examples.addtwodouble" => array(
763         "function" => "addTwoDouble",
764         "signature" => $addtwodouble_sig,
765         "docstring" => $addtwodouble_doc,
766     ),
767     "examples.stringecho" => array(
768         "function" => "stringEcho",
769         "signature" => $stringecho_sig,
770         "docstring" => $stringecho_doc,
771     ),
772     "examples.echo" => array(
773         "function" => "echoBack",
774         "signature" => $echoback_sig,
775         "docstring" => $echoback_doc,
776     ),
777     "examples.decode64" => array(
778         "function" => "echoSixtyFour",
779         "signature" => $echosixtyfour_sig,
780         "docstring" => $echosixtyfour_doc,
781     ),
782     "examples.invertBooleans" => array(
783         "function" => "bitFlipper",
784         "signature" => $bitflipper_sig,
785         "docstring" => $bitflipper_doc,
786     ),
787     // signature omitted on purpose
788     "tests.generatePHPWarning" => array(
789         "function" => array($object, "phpWarningGenerator"),
790     ),
791     // signature omitted on purpose
792     "tests.raiseException" => array(
793         "function" => array($object, "exceptionGenerator"),
794     ),
795     // Greek word 'kosme'. NB: NOT a valid ISO8859 string!
796     // NB: we can only register this when setting internal encoding to UTF-8, or it will break system.listMethods
797     "tests.utf8methodname." . 'κόσμε' => array(
798         "function" => "stringEcho",
799         "signature" => $stringecho_sig,
800         "docstring" => $stringecho_doc,
801     ),
802     /*"tests.iso88591methodname." . chr(224) . chr(252) . chr(232) => array(
803         "function" => "stringEcho",
804         "signature" => $stringecho_sig,
805         "docstring" => $stringecho_doc,
806     ),*/
807     "examples.getallheaders" => array(
808         "function" => 'getAllHeaders_xmlrpc',
809         "signature" => $getallheaders_sig,
810         "docstring" => $getallheaders_doc,
811     ),
812     "examples.setcookies" => array(
813         "function" => 'setCookies',
814         "signature" => $setcookies_sig,
815         "docstring" => $setcookies_doc,
816     ),
817     "examples.getcookies" => array(
818         "function" => 'getCookies',
819         "signature" => $getcookies_sig,
820         "docstring" => $getcookies_doc,
821     ),
822     "mail.send" => array(
823         "function" => "mailSend",
824         "signature" => $mailsend_sig,
825         "docstring" => $mailsend_doc,
826     ),
827     "validator1.arrayOfStructsTest" => array(
828         "function" => "v1_arrayOfStructs",
829         "signature" => $v1_arrayOfStructs_sig,
830         "docstring" => $v1_arrayOfStructs_doc,
831     ),
832     "validator1.easyStructTest" => array(
833         "function" => "v1_easyStruct",
834         "signature" => $v1_easyStruct_sig,
835         "docstring" => $v1_easyStruct_doc,
836     ),
837     "validator1.echoStructTest" => array(
838         "function" => "v1_echoStruct",
839         "signature" => $v1_echoStruct_sig,
840         "docstring" => $v1_echoStruct_doc,
841     ),
842     "validator1.manyTypesTest" => array(
843         "function" => "v1_manyTypes",
844         "signature" => $v1_manyTypes_sig,
845         "docstring" => $v1_manyTypes_doc,
846     ),
847     "validator1.moderateSizeArrayCheck" => array(
848         "function" => "v1_moderateSizeArrayCheck",
849         "signature" => $v1_moderateSizeArrayCheck_sig,
850         "docstring" => $v1_moderateSizeArrayCheck_doc,
851     ),
852     "validator1.simpleStructReturnTest" => array(
853         "function" => "v1_simpleStructReturn",
854         "signature" => $v1_simpleStructReturn_sig,
855         "docstring" => $v1_simpleStructReturn_doc,
856     ),
857     "validator1.nestedStructTest" => array(
858         "function" => "v1_nestedStruct",
859         "signature" => $v1_nestedStruct_sig,
860         "docstring" => $v1_nestedStruct_doc,
861     ),
862     "validator1.countTheEntities" => array(
863         "function" => "v1_countTheEntities",
864         "signature" => $v1_countTheEntities_sig,
865         "docstring" => $v1_countTheEntities_doc,
866     ),
867     "interopEchoTests.echoString" => array(
868         "function" => "i_echoString",
869         "signature" => $i_echoString_sig,
870         "docstring" => $i_echoString_doc,
871     ),
872     "interopEchoTests.echoStringArray" => array(
873         "function" => "i_echoStringArray",
874         "signature" => $i_echoStringArray_sig,
875         "docstring" => $i_echoStringArray_doc,
876     ),
877     "interopEchoTests.echoInteger" => array(
878         "function" => "i_echoInteger",
879         "signature" => $i_echoInteger_sig,
880         "docstring" => $i_echoInteger_doc,
881     ),
882     "interopEchoTests.echoIntegerArray" => array(
883         "function" => "i_echoIntegerArray",
884         "signature" => $i_echoIntegerArray_sig,
885         "docstring" => $i_echoIntegerArray_doc,
886     ),
887     "interopEchoTests.echoFloat" => array(
888         "function" => "i_echoFloat",
889         "signature" => $i_echoFloat_sig,
890         "docstring" => $i_echoFloat_doc,
891     ),
892     "interopEchoTests.echoFloatArray" => array(
893         "function" => "i_echoFloatArray",
894         "signature" => $i_echoFloatArray_sig,
895         "docstring" => $i_echoFloatArray_doc,
896     ),
897     "interopEchoTests.echoStruct" => array(
898         "function" => "i_echoStruct",
899         "signature" => $i_echoStruct_sig,
900         "docstring" => $i_echoStruct_doc,
901     ),
902     "interopEchoTests.echoStructArray" => array(
903         "function" => "i_echoStructArray",
904         "signature" => $i_echoStructArray_sig,
905         "docstring" => $i_echoStructArray_doc,
906     ),
907     "interopEchoTests.echoValue" => array(
908         "function" => "i_echoValue",
909         "signature" => $i_echoValue_sig,
910         "docstring" => $i_echoValue_doc,
911     ),
912     "interopEchoTests.echoBase64" => array(
913         "function" => "i_echoBase64",
914         "signature" => $i_echoBase64_sig,
915         "docstring" => $i_echoBase64_doc,
916     ),
917     "interopEchoTests.echoDate" => array(
918         "function" => "i_echoDate",
919         "signature" => $i_echoDate_sig,
920         "docstring" => $i_echoDate_doc,
921     ),
922     "interopEchoTests.whichToolkit" => array(
923         "function" => "i_whichToolkit",
924         "signature" => $i_whichToolkit_sig,
925         "docstring" => $i_whichToolkit_doc,
926     ),
927
928     'tests.getStateName.2' => $findstate2_sig,
929     'tests.getStateName.3' => $findstate3_sig,
930     'tests.getStateName.4' => $findstate4_sig,
931     'tests.getStateName.5' => $findstate5_sig,
932     'tests.getStateName.6' => $findstate6_sig,
933     'tests.getStateName.7' => $findstate7_sig,
934     'tests.getStateName.8' => $findstate8_sig,
935     'tests.getStateName.9' => $findstate9_sig,
936     'tests.getStateName.10' => $findstate10_sig,
937     'tests.getStateName.11' => $findstate11_sig,
938
939     'tests.getStateName.12' => array(
940         "function" => "findStateWithNulls",
941         "signature" => $findstate12_sig,
942         "docstring" => $findstate_doc,
943     ),
944
945     'tests.returnPhpObject' => $returnObj_sig,
946 );
947
948 $signatures = array_merge($signatures, $moreSignatures);
949
950 // Enable support for the NULL extension
951 PhpXmlRpc\PhpXmlRpc::$xmlrpc_null_extension = true;
952
953 $s = new PhpXmlRpc\Server($signatures, false);
954 $s->setdebug(3);
955 $s->compress_response = true;
956
957 // Out-of-band information: let the client manipulate the server operations.
958 // We do this to help the testsuite script: do not reproduce in production!
959 if (isset($_GET['RESPONSE_ENCODING'])) {
960     $s->response_charset_encoding = $_GET['RESPONSE_ENCODING'];
961 }
962 if (isset($_GET['DETECT_ENCODINGS'])) {
963     PhpXmlRpc\PhpXmlRpc::$xmlrpc_detectencodings = $_GET['DETECT_ENCODINGS'];
964 }
965 if (isset($_GET['EXCEPTION_HANDLING'])) {
966     $s->exception_handling = $_GET['EXCEPTION_HANDLING'];
967 }
968 if (isset($_GET['FORCE_AUTH'])) {
969     // We implement both  Basic and Digest auth in php to avoid having to set it up in a vhost.
970     // Code taken from php.net
971     // NB: we do NOT check for valid credentials!
972     if ($_GET['FORCE_AUTH'] == 'Basic') {
973         if (!isset($_SERVER['PHP_AUTH_USER']) && !isset($_SERVER['REMOTE_USER']) && !isset($_SERVER['REDIRECT_REMOTE_USER'])) {
974             header('HTTP/1.0 401 Unauthorized');
975             header('WWW-Authenticate: Basic realm="Phpxmlrpc Basic Realm"');
976             die('Text visible if user hits Cancel button');
977         }
978     } elseif ($_GET['FORCE_AUTH'] == 'Digest') {
979         if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
980             header('HTTP/1.1 401 Unauthorized');
981             header('WWW-Authenticate: Digest realm="Phpxmlrpc Digest Realm",qop="auth",nonce="'.uniqid().'",opaque="'.md5('Phpxmlrpc Digest Realm').'"');
982             die('Text visible if user hits Cancel button');
983         }
984     }
985 }
986
987 $s->service();
988 // That should do all we need!
989
990 // Out-of-band information: let the client manipulate the server operations.
991 // We do this to help the testsuite script: do not reproduce in production!
992 if (isset($_COOKIE['PHPUNIT_SELENIUM_TEST_ID']) && extension_loaded('xdebug')) {
993     include_once __DIR__ . "/../../vendor/phpunit/phpunit-selenium/PHPUnit/Extensions/SeleniumCommon/append.php";
994 }