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