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