4bbccba082e76620145daf3d2e9f25c183185e18
[plcapi.git] / tests / 1ParsingBugsTest.php
1 <?php
2 /**
3  * NB: do not let your IDE fool you. The correct encoding for this file is NOT UTF8.
4  */
5 include_once __DIR__ . '/../lib/xmlrpc.inc';
6 include_once __DIR__ . '/../lib/xmlrpcs.inc';
7
8 class ParsingBugsTests extends PHPUnit_Framework_TestCase
9 {
10     public $args = array();
11
12     public function setUp()
13     {
14         $this->args = argParser::getArgs();
15     }
16
17     protected function newMsg($methodName, $params = array())
18     {
19         $msg = new xmlrpcmsg($methodName, $params);
20         if ($this->args['DEBUG']) {
21             $msg->setDebug($this->args['DEBUG']);
22         }
23         return $msg;
24     }
25
26     public function testMinusOneString()
27     {
28         $v = new xmlrpcval('-1');
29         $u = new xmlrpcval('-1', 'string');
30         $t = new xmlrpcval(-1, 'string');
31         $this->assertEquals($u->scalarval(), $v->scalarval());
32         $this->assertEquals($t->scalarval(), $v->scalarval());
33     }
34
35     /**
36      * This looks funny, and we might call it a bug. But we strive for 100 backwards compat...
37      */
38     public function testMinusOneInt()
39     {
40         $v = new xmlrpcval(-1);
41         $u = new xmlrpcval();
42         $this->assertEquals($u->scalarval(), $v->scalarval());
43     }
44
45     public function testUnicodeInMemberName()
46     {
47         $str = "G" . chr(252) . "nter, El" . chr(232) . "ne";
48         $v = array($str => new xmlrpcval(1));
49         $r = new xmlrpcresp(new xmlrpcval($v, 'struct'));
50         $r = $r->serialize();
51         $m = $this->newMsg('dummy');
52         $r = $m->parseResponse($r);
53         $v = $r->value();
54         $this->assertEquals($v->structmemexists($str), true);
55     }
56
57     public function testUnicodeInErrorString()
58     {
59         $response = utf8_encode(
60             '<?xml version="1.0"?>
61 <!-- $Id -->
62 <!-- found by G. giunta, covers what happens when lib receives
63   UTF8 chars in response text and comments -->
64 <!-- ' . chr(224) . chr(252) . chr(232) . '&#224;&#252;&#232; -->
65 <methodResponse>
66 <fault>
67 <value>
68 <struct>
69 <member>
70 <name>faultCode</name>
71 <value><int>888</int></value>
72 </member>
73 <member>
74 <name>faultString</name>
75 <value><string>' . chr(224) . chr(252) . chr(232) . '&#224;&#252;&#232;</string></value>
76 </member>
77 </struct>
78 </value>
79 </fault>
80 </methodResponse>');
81         $m = $this->newMsg('dummy');
82         $r = $m->parseResponse($response);
83         $v = $r->faultString();
84         $this->assertEquals(chr(224) . chr(252) . chr(232) . chr(224) . chr(252) . chr(232), $v);
85     }
86
87     public function testValidNumbers()
88     {
89         $m = $this->newMsg('dummy');
90         $fp =
91             '<?xml version="1.0"?>
92 <methodResponse>
93 <params>
94 <param>
95 <value>
96 <struct>
97 <member>
98 <name>integer1</name>
99 <value><int>01</int></value>
100 </member>
101 <member>
102 <name>float1</name>
103 <value><double>01.10</double></value>
104 </member>
105 <member>
106 <name>integer2</name>
107 <value><int>+1</int></value>
108 </member>
109 <member>
110 <name>float2</name>
111 <value><double>+1.10</double></value>
112 </member>
113 <member>
114 <name>float3</name>
115 <value><double>-1.10e2</double></value>
116 </member>
117 </struct>
118 </value>
119 </param>
120 </params>
121 </methodResponse>';
122         $r = $m->parseResponse($fp);
123         $v = $r->value();
124         $s = $v->structmem('integer1');
125         $t = $v->structmem('float1');
126         $u = $v->structmem('integer2');
127         $w = $v->structmem('float2');
128         $x = $v->structmem('float3');
129         $this->assertEquals(1, $s->scalarval());
130         $this->assertEquals(1.1, $t->scalarval());
131         $this->assertEquals(1, $u->scalarval());
132         $this->assertEquals(1.1, $w->scalarval());
133         $this->assertEquals(-110.0, $x->scalarval());
134     }
135
136     public function testAddScalarToStruct()
137     {
138         $v = new xmlrpcval(array('a' => 'b'), 'struct');
139         // use @ operator in case error_log gets on screen
140         $r = @$v->addscalar('c');
141         $this->assertEquals(0, $r);
142     }
143
144     public function testAddStructToStruct()
145     {
146         $v = new xmlrpcval(array('a' => new xmlrpcval('b')), 'struct');
147         $r = $v->addstruct(array('b' => new xmlrpcval('c')));
148         $this->assertEquals(2, $v->structsize());
149         $this->assertEquals(1, $r);
150         $r = $v->addstruct(array('b' => new xmlrpcval('b')));
151         $this->assertEquals(2, $v->structsize());
152     }
153
154     public function testAddArrayToArray()
155     {
156         $v = new xmlrpcval(array(new xmlrpcval('a'), new xmlrpcval('b')), 'array');
157         $r = $v->addarray(array(new xmlrpcval('b'), new xmlrpcval('c')));
158         $this->assertEquals(4, $v->arraysize());
159         $this->assertEquals(1, $r);
160     }
161
162     public function testEncodeArray()
163     {
164         $r = range(1, 100);
165         $v = php_xmlrpc_encode($r);
166         $this->assertEquals('array', $v->kindof());
167     }
168
169     public function testEncodeRecursive()
170     {
171         $v = php_xmlrpc_encode(php_xmlrpc_encode('a simple string'));
172         $this->assertEquals('scalar', $v->kindof());
173     }
174
175     public function testBrokenRequests()
176     {
177         $s = new xmlrpc_server();
178         // omitting the 'params' tag: not tolerated by the lib anymore
179         $f = '<?xml version="1.0"?>
180 <methodCall>
181 <methodName>system.methodHelp</methodName>
182 <param>
183 <value><string>system.methodHelp</string></value>
184 </param>
185 </methodCall>';
186         $r = $s->parserequest($f);
187         $this->assertEquals(15, $r->faultCode());
188         // omitting a 'param' tag
189         $f = '<?xml version="1.0"?>
190 <methodCall>
191 <methodName>system.methodHelp</methodName>
192 <params>
193 <value><string>system.methodHelp</string></value>
194 </params>
195 </methodCall>';
196         $r = $s->parserequest($f);
197         $this->assertEquals(15, $r->faultCode());
198         // omitting a 'value' tag
199         $f = '<?xml version="1.0"?>
200 <methodCall>
201 <methodName>system.methodHelp</methodName>
202 <params>
203 <param><string>system.methodHelp</string></param>
204 </params>
205 </methodCall>';
206         $r = $s->parserequest($f);
207         $this->assertEquals(15, $r->faultCode());
208     }
209
210     public function testBrokenResponses()
211     {
212         $m = $this->newMsg('dummy');
213         // omitting the 'params' tag: no more tolerated by the lib...
214         $f = '<?xml version="1.0"?>
215 <methodResponse>
216 <param>
217 <value><string>system.methodHelp</string></value>
218 </param>
219 </methodResponse>';
220         $r = $m->parseResponse($f);
221         $this->assertEquals(2, $r->faultCode());
222         // omitting the 'param' tag: no more tolerated by the lib...
223         $f = '<?xml version="1.0"?>
224 <methodResponse>
225 <params>
226 <value><string>system.methodHelp</string></value>
227 </params>
228 </methodResponse>';
229         $r = $m->parseResponse($f);
230         $this->assertEquals(2, $r->faultCode());
231         // omitting a 'value' tag: KO
232         $f = '<?xml version="1.0"?>
233 <methodResponse>
234 <params>
235 <param><string>system.methodHelp</string></param>
236 </params>
237 </methodResponse>';
238         $r = $m->parseResponse($f);
239         $this->assertEquals(2, $r->faultCode());
240     }
241
242     public function testBuggyHttp()
243     {
244         $s = $this->newMsg('dummy');
245         $f = 'HTTP/1.1 100 Welcome to the jungle
246
247 HTTP/1.0 200 OK
248 X-Content-Marx-Brothers: Harpo
249         Chico and Groucho
250 Content-Length: who knows?
251
252
253
254 <?xml version="1.0"?>
255 <!-- First of all, let\'s check out if the lib properly handles a commented </methodResponse> tag... -->
256 <methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
257 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
258
259
260 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
261 <script type="text\javascript">document.write(\'Hello, my name is added nag, I\\\'m happy to serve your content for free\');</script>
262  ';
263         $r = $s->parseResponse($f);
264         $v = $r->value();
265         $s = $v->structmem('content');
266         $this->assertEquals("hello world. 2 newlines follow\n\n\nand there they were.", $s->scalarval());
267     }
268
269     public function testStringBug()
270     {
271         $s = $this->newMsg('dummy');
272         $f = '<?xml version="1.0"?>
273 <!-- $Id -->
274 <!-- found by 2z69xks7bpy001@sneakemail.com, amongst others
275  covers what happens when there\'s character data after </string>
276  and before </value> -->
277 <methodResponse>
278 <params>
279 <param>
280 <value>
281 <struct>
282 <member>
283 <name>success</name>
284 <value>
285 <boolean>1</boolean>
286 </value>
287 </member>
288 <member>
289 <name>sessionID</name>
290 <value>
291 <string>S300510007I</string>
292 </value>
293 </member>
294 </struct>
295 </value>
296 </param>
297 </params>
298 </methodResponse> ';
299         $r = $s->parseResponse($f);
300         $v = $r->value();
301         $s = $v->structmem('sessionID');
302         $this->assertEquals('S300510007I', $s->scalarval());
303     }
304
305     public function testWhiteSpace()
306     {
307         $s = $this->newMsg('dummy');
308         $f = '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
309 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
310
311
312 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
313 ';
314         $r = $s->parseResponse($f);
315         $v = $r->value();
316         $s = $v->structmem('content');
317         $this->assertEquals("hello world. 2 newlines follow\n\n\nand there they were.", $s->scalarval());
318     }
319
320     public function testDoubleDataInArrayTag()
321     {
322         $s = $this->newMsg('dummy');
323         $f = '<?xml version="1.0"?><methodResponse><params><param><value><array>
324 <data></data>
325 <data></data>
326 </array></value></param></params></methodResponse>
327 ';
328         $r = $s->parseResponse($f);
329         $v = $r->faultCode();
330         $this->assertEquals(2, $v);
331         $f = '<?xml version="1.0"?><methodResponse><params><param><value><array>
332 <data><value>Hello world</value></data>
333 <data></data>
334 </array></value></param></params></methodResponse>
335 ';
336         $r = $s->parseResponse($f);
337         $v = $r->faultCode();
338         $this->assertEquals(2, $v);
339     }
340
341     public function testDoubleStuffInValueTag()
342     {
343         $s = $this->newMsg('dummy');
344         $f = '<?xml version="1.0"?><methodResponse><params><param><value>
345 <string>hello world</string>
346 <array><data></data></array>
347 </value></param></params></methodResponse>
348 ';
349         $r = $s->parseResponse($f);
350         $v = $r->faultCode();
351         $this->assertEquals(2, $v);
352         $f = '<?xml version="1.0"?><methodResponse><params><param><value>
353 <string>hello</string>
354 <string>world</string>
355 </value></param></params></methodResponse>
356 ';
357         $r = $s->parseResponse($f);
358         $v = $r->faultCode();
359         $this->assertEquals(2, $v);
360         $f = '<?xml version="1.0"?><methodResponse><params><param><value>
361 <string>hello</string>
362 <struct><member><name>hello><value>world</value></member></struct>
363 </value></param></params></methodResponse>
364 ';
365         $r = $s->parseResponse($f);
366         $v = $r->faultCode();
367         $this->assertEquals(2, $v);
368     }
369
370     public function testAutodecodeResponse()
371     {
372         $s = $this->newMsg('dummy');
373         $f = '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
374 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
375
376
377 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
378 ';
379         $r = $s->parseResponse($f, true, 'phpvals');
380         $v = $r->value();
381         $s = $v['content'];
382         $this->assertEquals("hello world. 2 newlines follow\n\n\nand there they were.", $s);
383     }
384
385     public function testNoDecodeResponse()
386     {
387         $s = $this->newMsg('dummy');
388         $f = '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
389 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>hello world. 2 newlines follow
390
391
392 and there they were.</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>';
393         $r = $s->parseResponse($f, true, 'xml');
394         $v = $r->value();
395         $this->assertEquals($f, $v);
396     }
397
398     public function testAutoCoDec()
399     {
400         $data1 = array(1, 1.0, 'hello world', true, '20051021T23:43:00', -1, 11.0, '~!@#$%^&*()_+|', false, '20051021T23:43:00');
401         $data2 = array('zero' => $data1, 'one' => $data1, 'two' => $data1, 'three' => $data1, 'four' => $data1, 'five' => $data1, 'six' => $data1, 'seven' => $data1, 'eight' => $data1, 'nine' => $data1);
402         $data = array($data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2, $data2);
403         //$keys = array('zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine');
404         $v1 = php_xmlrpc_encode($data, array('auto_dates'));
405         $v2 = php_xmlrpc_decode_xml($v1->serialize());
406         $this->assertEquals($v1, $v2);
407         $r1 = new PhpXmlRpc\Response($v1);
408         $r2 = php_xmlrpc_decode_xml($r1->serialize());
409         $r2->serialize(); // needed to set internal member payload
410         $this->assertEquals($r1, $r2);
411         $m1 = new PhpXmlRpc\Request('hello dolly', array($v1));
412         $m2 = php_xmlrpc_decode_xml($m1->serialize());
413         $m2->serialize(); // needed to set internal member payload
414         $this->assertEquals($m1, $m2);
415     }
416
417     public function testUTF8Request()
418     {
419         $sendstring = 'κόσμε'; // Greek word 'kosme'. NB: NOT a valid ISO8859 string!
420         $GLOBALS['xmlrpc_internalencoding'] = 'UTF-8';
421         \PhpXmlRpc\PhpXmlRpc::importGlobals();
422         $f = new xmlrpcval($sendstring, 'string');
423         $v = $f->serialize();
424         $this->assertEquals("<value><string>&#954;&#8057;&#963;&#956;&#949;</string></value>\n", $v);
425         $GLOBALS['xmlrpc_internalencoding'] = 'ISO-8859-1';
426         \PhpXmlRpc\PhpXmlRpc::importGlobals();
427     }
428
429     public function testUTF8Response()
430     {
431         $string = chr(224) . chr(252) . chr(232);
432
433         $s = $this->newMsg('dummy');
434         $f = "HTTP/1.1 200 OK\r\nContent-type: text/xml; charset=UTF-8\r\n\r\n" . '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
435 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>' . utf8_encode($string) . '</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
436 ';
437         $r = $s->parseResponse($f, false, 'phpvals');
438         $v = $r->value();
439         $v = $v['content'];
440         $this->assertEquals($string, $v);
441
442         $f = '<?xml version="1.0" encoding="utf-8"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
443 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>' . utf8_encode($string) . '</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
444 ';
445         $r = $s->parseResponse($f, false, 'phpvals');
446         $v = $r->value();
447         $v = $v['content'];
448         $this->assertEquals($string, $v);
449
450         $r = php_xmlrpc_decode_xml($f);
451         $v = $r->value();
452         $v = $v->structmem('content')->scalarval();
453         $this->assertEquals($string, $v);
454     }
455
456     public function testLatin1Response()
457     {
458         $string = chr(224) . chr(252) . chr(232);
459
460         $s = $this->newMsg('dummy');
461         $f = "HTTP/1.1 200 OK\r\nContent-type: text/xml; charset=ISO-8859-1\r\n\r\n" . '<?xml version="1.0"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
462 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>' . $string . '</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
463 ';
464         $r = $s->parseResponse($f, false, 'phpvals');
465         $v = $r->value();
466         $v = $v['content'];
467         $this->assertEquals($string, $v);
468
469         $f = '<?xml version="1.0" encoding="iso-8859-1"?><methodResponse><params><param><value><struct><member><name>userid</name><value>311127</value></member>
470 <member><name>dateCreated</name><value><dateTime.iso8601>20011126T09:17:52</dateTime.iso8601></value></member><member><name>content</name><value>' . $string . '</value></member><member><name>postid</name><value>7414222</value></member></struct></value></param></params></methodResponse>
471 ';
472         $r = $s->parseResponse($f, false, 'phpvals');
473         $v = $r->value();
474         $v = $v['content'];
475         $this->assertEquals($string, $v);
476
477         $r = php_xmlrpc_decode_xml($f);
478         $v = $r->value();
479         $v = $v->structmem('content')->scalarval();
480         $this->assertEquals($string, $v);
481     }
482
483     public function testUTF8IntString()
484     {
485         $v = new xmlrpcval(100, 'int');
486         $s = $v->serialize('UTF-8');
487         $this->assertequals("<value><int>100</int></value>\n", $s);
488     }
489
490     public function testStringInt()
491     {
492         $v = new xmlrpcval('hello world', 'int');
493         $s = $v->serialize();
494         $this->assertequals("<value><int>0</int></value>\n", $s);
495     }
496
497     public function testStructMemExists()
498     {
499         $v = php_xmlrpc_encode(array('hello' => 'world'));
500         $b = $v->structmemexists('hello');
501         $this->assertequals(true, $b);
502         $b = $v->structmemexists('world');
503         $this->assertequals(false, $b);
504     }
505
506     public function testNilvalue()
507     {
508         // default case: we do not accept nil values received
509         $v = new xmlrpcval('hello', 'null');
510         $r = new xmlrpcresp($v);
511         $s = $r->serialize();
512         $m = $this->newMsg('dummy');
513         $r = $m->parseresponse($s);
514         $this->assertequals(2, $r->faultCode());
515         // enable reception of nil values
516         $GLOBALS['xmlrpc_null_extension'] = true;
517         \PhpXmlRpc\PhpXmlRpc::importGlobals();
518         $r = $m->parseresponse($s);
519         $v = $r->value();
520         $this->assertequals('null', $v->scalartyp());
521         // test with the apache version: EX:NIL
522         $GLOBALS['xmlrpc_null_apache_encoding'] = true;
523         \PhpXmlRpc\PhpXmlRpc::importGlobals();
524         // serialization
525         $v = new xmlrpcval('hello', 'null');
526         $s = $v->serialize();
527         $this->assertequals(1, preg_match('#<value><ex:nil/></value>#', $s));
528         // deserialization
529         $r = new xmlrpcresp($v);
530         $s = $r->serialize();
531         $r = $m->parseresponse($s);
532         $v = $r->value();
533         $this->assertequals('null', $v->scalartyp());
534         $GLOBALS['xmlrpc_null_extension'] = false;
535         \PhpXmlRpc\PhpXmlRpc::importGlobals();
536         $r = $m->parseresponse($s);
537         $this->assertequals(2, $r->faultCode());
538     }
539
540     public function TestLocale()
541     {
542         $locale = setlocale(LC_NUMERIC, 0);
543         /// @todo on php 5.3/win setting locale to german does not seem to set decimal separator to comma...
544         if (setlocale(LC_NUMERIC, 'deu', 'de_DE@euro', 'de_DE', 'de', 'ge') !== false) {
545             $v = new xmlrpcval(1.1, 'double');
546             if (strpos($v->scalarval(), ',') == 1) {
547                 $r = $v->serialize();
548                 $this->assertequals(false, strpos($r, ','));
549                 setlocale(LC_NUMERIC, $locale);
550             } else {
551                 setlocale(LC_NUMERIC, $locale);
552                 $this->markTestSkipped('did not find a locale which sets decimal separator to comma');
553             }
554         } else {
555             $this->markTestSkipped('did not find a locale which sets decimal separator to comma');
556         }
557     }
558 }