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