build on PRs; nitpicks
[plcapi.git] / demo / server / methodProviders / functions.php
1 <?php
2 /**
3  * Defines functions and signatures which can be registered as methods exposed by an XMLRPC Server
4  *
5  * To use this, use something akin to:
6  * $signatures = include('functions.php');
7  *
8  * Simplest possible way to implement webservices: create xmlrpc-aware php functions in the global namespace
9  */
10
11 use PhpXmlRpc\Encoder;
12 use PhpXmlRpc\Response;
13 use PhpXmlRpc\Server;
14 use PhpXmlRpc\Value;
15
16 // a PHP version of the state-number server
17 // send me an integer and i'll sell you a state
18
19 $GLOBALS['stateNames'] = array(
20     "Alabama", "Alaska", "Arizona", "Arkansas", "California",
21     "Colorado", "Columbia", "Connecticut", "Delaware", "Florida",
22     "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas",
23     "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan",
24     "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada",
25     "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina",
26     "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island",
27     "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont",
28     "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming",
29 );
30
31 $findstate_sig = array(array(Value::$xmlrpcString, Value::$xmlrpcInt));
32 $findstate_doc = 'When passed an integer between 1 and 51 returns the name of a US state, where the integer is the ' .
33     'index of that state name in an alphabetic order.';
34 function findState($req)
35 {
36     $err = "";
37     // get the first param
38     $sno = $req->getParam(0);
39
40     // param must be there and of the correct type: server object does the validation for us
41
42     // extract the value of the state number
43     $snv = $sno->scalarval();
44     // look it up in our array (zero-based)
45     if (isset($GLOBALS['stateNames'][$snv - 1])) {
46         $stateName = $GLOBALS['stateNames'][$snv - 1];
47     } else {
48         // not there, so complain
49         $err = "I don't have a state for the index '" . $snv . "'";
50     }
51
52     // if we generated an error, create an error return response
53     if ($err) {
54         return new Response(0, PhpXmlRpc\PhpXmlRpc::$xmlrpcerruser, $err);
55     } else {
56         // otherwise, we create the right response with the state name
57         return new Response(new Value($stateName));
58     }
59 }
60
61 // Sorting demo
62 //
63 // send me an array of structs thus:
64 //
65 // Dave 35
66 // Edd  45
67 // Fred 23
68 // Barney 37
69 //
70 // and I'll return it to you in sorted order
71
72 function agesorter_compare($a, $b)
73 {
74     /// @todo move away from usage of globals for such a simple case
75     global $agesorter_arr;
76
77     // don't even ask me _why_ these come padded with hyphens, I couldn't tell you :p
78     $a = str_replace("-", "", $a);
79     $b = str_replace("-", "", $b);
80
81     if ($agesorter_arr[$a] == $agesorter_arr[$b]) {
82         return 0;
83     }
84
85     return ($agesorter_arr[$a] > $agesorter_arr[$b]) ? -1 : 1;
86 }
87
88 $agesorter_sig = array(array(Value::$xmlrpcArray, Value::$xmlrpcArray));
89 $agesorter_doc = 'Send this method an array of [string, int] structs, eg:
90 <pre>
91  Dave   35
92  Edd    45
93  Fred   23
94  Barney 37
95 </pre>
96 And the array will be returned with the entries sorted by their numbers.
97 ';
98 function ageSorter($req)
99 {
100     global $agesorter_arr;
101
102     Server::xmlrpc_debugmsg("Entering 'agesorter'");
103     // get the parameter
104     $sno = $req->getParam(0);
105     // error string for [if|when] things go wrong
106     $err = "";
107     $agar = array();
108
109     $max = $sno->count();
110     Server::xmlrpc_debugmsg("Found $max array elements");
111     foreach ($sno as $i => $rec) {
112         if ($rec->kindOf() != "struct") {
113             $err = "Found non-struct in array at element $i";
114             break;
115         }
116         // extract name and age from struct
117         $n = $rec["name"];
118         $a = $rec["age"];
119         // $n and $a are Values,
120         // so get the scalarval from them
121         $agar[$n->scalarval()] = $a->scalarval();
122     }
123
124     // create the output value
125     $v = new Value(array(), Value::$xmlrpcArray);
126
127     $agesorter_arr = $agar;
128     // hack, must make global as uksort() won't
129     // allow us to pass any other auxiliary information
130     uksort($agesorter_arr, 'agesorter_compare');
131     foreach($agesorter_arr as $key => $val) {
132         // recreate each struct element
133         $v[] = new Value(
134             array(
135                 "name" => new Value($key),
136                 "age" => new Value($val, "int")
137             ),
138             Value::$xmlrpcStruct
139         );
140     }
141
142     if ($err) {
143         return new Response(0, PhpXmlRpc\PhpXmlRpc::$xmlrpcerruser, $err);
144     } else {
145         return new Response($v);
146     }
147 }
148
149 $addtwo_sig = array(array(Value::$xmlrpcInt, Value::$xmlrpcInt, Value::$xmlrpcInt));
150 $addtwo_doc = 'Add two integers together and return the result';
151 function addTwo($req)
152 {
153     $s = $req->getParam(0);
154     $t = $req->getParam(1);
155
156     return new Response(new Value($s->scalarval() + $t->scalarval(), Value::$xmlrpcInt));
157 }
158
159 $addtwodouble_sig = array(array(Value::$xmlrpcDouble, Value::$xmlrpcDouble, Value::$xmlrpcDouble));
160 $addtwodouble_doc = 'Add two doubles together and return the result';
161 function addTwoDouble($req)
162 {
163     $s = $req->getParam(0);
164     $t = $req->getParam(1);
165
166     return new Response(new Value($s->scalarval() + $t->scalarval(), Value::$xmlrpcDouble));
167 }
168
169 $stringecho_sig = array(array(Value::$xmlrpcString, Value::$xmlrpcString));
170 $stringecho_doc = 'Accepts a string parameter, returns the string.';
171 function stringEcho($req)
172 {
173     // just sends back a string
174     return new Response(new Value($req->getParam(0)->scalarval()));
175 }
176
177 $echoback_sig = array(array(Value::$xmlrpcString, Value::$xmlrpcString));
178 $echoback_doc = 'Accepts a string parameter, returns the entire incoming payload';
179 function echoBack($req)
180 {
181     // just sends back a string with what i got sent to me, just escaped, that's all
182     $s = "I got the following message:\n" . $req->serialize();
183
184     return new Response(new Value($s));
185 }
186
187 $echosixtyfour_sig = array(array(Value::$xmlrpcString, Value::$xmlrpcBase64));
188 $echosixtyfour_doc = 'Accepts a base64 parameter and returns it decoded as a string';
189 function echoSixtyFour($req)
190 {
191     // Accepts an encoded value, but sends it back as a normal string.
192     // This is to test that base64 encoding is working as expected
193     $incoming = $req->getParam(0);
194
195     return new Response(new Value($incoming->scalarval(), Value::$xmlrpcString));
196 }
197
198 $bitflipper_sig = array(array(Value::$xmlrpcArray, Value::$xmlrpcArray));
199 $bitflipper_doc = 'Accepts an array of booleans, and returns them inverted';
200 function bitFlipper($req)
201 {
202     $v = $req->getParam(0);
203     $rv = new Value(array(), Value::$xmlrpcArray);
204
205     foreach ($v as $b) {
206         if ($b->scalarval()) {
207             $rv[] = new Value(false, Value::$xmlrpcBoolean);
208         } else {
209             $rv[] = new Value(true, Value::$xmlrpcBoolean);
210         }
211     }
212
213     return new Response($rv);
214 }
215
216 $getallheaders_sig = array(array(Value::$xmlrpcStruct));
217 $getallheaders_doc = 'Returns a struct containing all the HTTP headers received with the request. Provides limited functionality with IIS';
218 function getAllHeaders_xmlrpc($req)
219 {
220     $encoder = new Encoder();
221
222     if (function_exists('getallheaders')) {
223         return new Response($encoder->encode(getallheaders()));
224     } else {
225         $headers = array();
226         // IIS: poor man's version of getallheaders
227         foreach ($_SERVER as $key => $val) {
228             if (strpos($key, 'HTTP_') === 0) {
229                 $key = ucfirst(str_replace('_', '-', strtolower(substr($key, 5))));
230                 $headers[$key] = $val;
231             }
232         }
233
234         return new Response($encoder->encode($headers));
235     }
236 }
237
238 $setcookies_sig = array(array(Value::$xmlrpcInt, Value::$xmlrpcStruct));
239 $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)';
240 function setCookies($req)
241 {
242     $encoder = new Encoder();
243     $cookies = $req->getParam(0);
244     foreach ($cookies as $name => $value) {
245         $cookieDesc = $encoder->decode($value);
246         setcookie($name, @$cookieDesc['value'], @$cookieDesc['expires'], @$cookieDesc['path'], @$cookieDesc['domain'], @$cookieDesc['secure']);
247     }
248
249     return new Response(new Value(1, Value::$xmlrpcInt));
250 }
251
252 $getcookies_sig = array(array(Value::$xmlrpcStruct));
253 $getcookies_doc = 'Sends to client a response containing all http cookies as received in the request (as struct)';
254 function getCookies($req)
255 {
256     $encoder = new Encoder();
257     return new Response($encoder->encode($_COOKIE));
258 }
259
260 $mailsend_sig = array(array(
261     Value::$xmlrpcBoolean, Value::$xmlrpcString, Value::$xmlrpcString,
262     Value::$xmlrpcString, Value::$xmlrpcString, Value::$xmlrpcString,
263     Value::$xmlrpcString, Value::$xmlrpcString,
264 ));
265 $mailsend_doc = 'mail.send(recipient, subject, text, sender, cc, bcc, mimetype)<br/>
266 recipient, cc, and bcc are strings, comma-separated lists of email addresses, as described above.<br/>
267 subject is a string, the subject of the message.<br/>
268 sender is a string, it\'s the email address of the person sending the message. This string can not be
269 a comma-separated list, it must contain a single email address only.<br/>
270 text is a string, it contains the body of the message.<br/>
271 mimetype, a string, is a standard MIME type, for example, text/plain.
272 ';
273 // WARNING; this functionality depends on the sendmail -t option
274 // it may not work with Windows machines properly; particularly
275 // the Bcc option. Sneak on your friends at your own risk!
276 function mailSend($req)
277 {
278     $err = "";
279
280     $mTo = $req->getParam(0);
281     $mSub = $req->getParam(1);
282     $mBody = $req->getParam(2);
283     $mFrom = $req->getParam(3);
284     $mCc = $req->getParam(4);
285     $mBcc = $req->getParam(5);
286     $mMime = $req->getParam(6);
287
288     if ($mTo->scalarval() == "") {
289         $err = "Error, no 'To' field specified";
290     }
291
292     if ($mFrom->scalarval() == "") {
293         $err = "Error, no 'From' field specified";
294     }
295
296     $msgHdr = "From: " . $mFrom->scalarval() . "\n";
297     $msgHdr .= "To: " . $mTo->scalarval() . "\n";
298
299     if ($mCc->scalarval() != "") {
300         $msgHdr .= "Cc: " . $mCc->scalarval() . "\n";
301     }
302     if ($mBcc->scalarval() != "") {
303         $msgHdr .= "Bcc: " . $mBcc->scalarval() . "\n";
304     }
305     if ($mMime->scalarval() != "") {
306         $msgHdr .= "Content-type: " . $mMime->scalarval() . "\n";
307     }
308     $msgHdr .= "X-Mailer: XML-RPC for PHP mailer 1.0";
309
310     if ($err == "") {
311         if (!mail("", $mSub->scalarval(), $mBody->scalarval(), $msgHdr)
312         ) {
313             $err = "Error, could not send the mail.";
314         }
315     }
316
317     if ($err) {
318         return new Response(0, PhpXmlRpc\PhpXmlRpc::$xmlrpcerruser, $err);
319     } else {
320         return new Response(new Value(true, Value::$xmlrpcBoolean));
321     }
322 }
323
324 return array(
325     "examples.getStateName" => array(
326         "function" => "findState",
327         "signature" => $findstate_sig,
328         "docstring" => $findstate_doc,
329     ),
330     "examples.sortByAge" => array(
331         "function" => "ageSorter",
332         "signature" => $agesorter_sig,
333         "docstring" => $agesorter_doc,
334     ),
335     "examples.addtwo" => array(
336         "function" => "addTwo",
337         "signature" => $addtwo_sig,
338         "docstring" => $addtwo_doc,
339     ),
340     "examples.addtwodouble" => array(
341         "function" => "addTwoDouble",
342         "signature" => $addtwodouble_sig,
343         "docstring" => $addtwodouble_doc,
344     ),
345     "examples.stringecho" => array(
346         "function" => "stringEcho",
347         "signature" => $stringecho_sig,
348         "docstring" => $stringecho_doc,
349     ),
350     "examples.echo" => array(
351         "function" => "echoBack",
352         "signature" => $echoback_sig,
353         "docstring" => $echoback_doc,
354     ),
355     "examples.decode64" => array(
356         "function" => "echoSixtyFour",
357         "signature" => $echosixtyfour_sig,
358         "docstring" => $echosixtyfour_doc,
359     ),
360     "examples.invertBooleans" => array(
361         "function" => "bitFlipper",
362         "signature" => $bitflipper_sig,
363         "docstring" => $bitflipper_doc,
364     ),
365
366     "examples.getallheaders" => array(
367         "function" => 'getAllHeaders_xmlrpc',
368         "signature" => $getallheaders_sig,
369         "docstring" => $getallheaders_doc,
370     ),
371     "examples.setcookies" => array(
372         "function" => 'setCookies',
373         "signature" => $setcookies_sig,
374         "docstring" => $setcookies_doc,
375     ),
376     "examples.getcookies" => array(
377         "function" => 'getCookies',
378         "signature" => $getcookies_sig,
379         "docstring" => $getcookies_doc,
380     ),
381
382     "mail.send" => array(
383         "function" => "mailSend",
384         "signature" => $mailsend_sig,
385         "docstring" => $mailsend_doc,
386     ),
387 );