Fixes to make the debugger work
[plcapi.git] / debugger / action.php
1 <?php
2 /**
3  * @author Gaetano Giunta
4  * @copyright (C) 2005-2014 G. Giunta
5  * @license code licensed under the BSD License: http://phpxmlrpc.sourceforge.net/license.txt
6  *
7  * @todo switch params for http compression from 0,1,2 to values to be used directly
8  * @todo use ob_start to catch debug info and echo it AFTER method call results?
9  * @todo be smarter in creating client stub for proxy/auth cases: only set appropriate property of client obj
10  **/
11
12 ?>
13 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
14     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
15 <html xmlns="http://www.w3.org/1999/xhtml">
16 <head>
17   <title>XMLRPC Debugger</title>
18   <meta name="robots" content="index,nofollow" />
19 <style type="text/css">
20 <!--
21 body {border-top: 1px solid gray; padding: 1em; font-family: Verdana, Arial, Helvetica; font-size: 8pt;}
22 h3 {font-size: 9.5pt;}
23 h2 {font-size: 12pt;}
24 .dbginfo {padding: 1em; background-color: #EEEEEE; border: 1px dashed silver; font-family: monospace;}
25 #response {padding: 1em; margin-top: 1em; background-color: #DDDDDD; border: 1px solid gray; white-space: pre; font-family: monospace;}
26 table {padding: 2px; margin-top: 1em;}
27 th {background-color: navy; color: white; padding: 0.5em;}
28 td {padding: 0.5em; font-family: monospace;}
29 td form {margin: 0;}
30 .oddrow {background-color: #EEEEEE;}
31 .evidence {color: blue;}
32 #phpcode { background-color: #EEEEEE; padding: 1em; margin-top: 1em;}
33 -->
34 </style>
35 </head>
36 <body>
37 <?php
38
39   include(__DIR__.'/common.php');
40   if ($action)
41   {
42
43     // make sure the script waits long enough for the call to complete...
44     if ($timeout)
45       set_time_limit($timeout+10);
46
47     include('xmlrpc.inc');
48     if ($wstype == 1)
49     {
50       @include('jsonrpc.inc');
51       if (!class_exists('jsonrpc_client'))
52       {
53         die('Error: to debug the jsonrpc protocol the jsonrpc.inc file is needed');
54       }
55       $clientclass = 'jsonrpc_client';
56       $msgclass = 'jsonrpcmsg';
57       $protoname = 'JSONRPC';
58     }
59     else
60     {
61       $clientclass = 'xmlrpc_client';
62       $msgclass = 'xmlrpcmsg';
63       $protoname = 'XMLRPC';
64     }
65
66     if ($port != "")
67     {
68       $client = new $clientclass($path, $host, $port);
69       $server = "$host:$port$path";
70     } else {
71       $client = new $clientclass($path, $host);
72       $server = "$host$path";
73     }
74     if ($protocol == 2)
75     {
76       $server = 'https://'.$server;
77     }
78     else
79     {
80       $server = 'http://'.$server;
81     }
82     if ($proxy != '') {
83       $pproxy = explode(':', $proxy);
84       if (count($pproxy) > 1)
85         $pport = $pproxy[1];
86       else
87         $pport = 8080;
88       $client->setProxy($pproxy[0], $pport, $proxyuser, $proxypwd);
89     }
90
91     if ($protocol == 2)
92     {
93       $client->setSSLVerifyPeer($verifypeer);
94       $client->setSSLVerifyHost($verifyhost);
95       if ($cainfo)
96       {
97         $client->setCaCertificate($cainfo);
98       }
99       $httpprotocol = 'https';
100     }
101     else if ($protocol == 1)
102       $httpprotocol = 'http11';
103     else
104       $httpprotocol = 'http';
105
106     if ($username)
107       $client->setCredentials($username, $password, $authtype);
108
109     $client->setDebug($debug);
110
111     switch ($requestcompression) {
112       case 0:
113         $client->request_compression = '';
114         break;
115       case 1:
116         $client->request_compression = 'gzip';
117         break;
118       case 2:
119         $client->request_compression = 'deflate';
120         break;
121     }
122
123     switch ($responsecompression) {
124       case 0:
125         $client->accepted_compression = '';
126         break;
127       case 1:
128         $client->accepted_compression = array('gzip');
129         break;
130       case 2:
131         $client->accepted_compression = array('deflate');
132         break;
133       case 3:
134         $client->accepted_compression = array('gzip', 'deflate');
135         break;
136     }
137
138     $cookies = explode(',', $clientcookies);
139     foreach ($cookies as $cookie)
140     {
141       if (strpos($cookie, '='))
142       {
143         $cookie = explode('=', $cookie);
144         $client->setCookie(trim($cookie[0]), trim(@$cookie[1]));
145       }
146     }
147
148     $msg = array();
149     switch ($action) {
150
151       case 'wrap':
152         @include('xmlrpc_wrappers.inc');
153         if (!function_exists('build_remote_method_wrapper_code'))
154         {
155           die('Error: to enable creation of method stubs the xmlrpc_wrappers.inc file is needed');
156         }
157         // fall thru intentionally
158       case 'describe':
159       case 'wrap':
160         $msg[0] = new $msgclass('system.methodHelp', array(), $id);
161         $msg[0]->addparam(new xmlrpcval($method));
162         $msg[1] = new $msgclass('system.methodSignature', array(), $id+1);
163         $msg[1]->addparam(new xmlrpcval($method));
164         $actionname = 'Description of method "'.$method.'"';
165         break;
166       case 'list':
167         $msg[0] = new $msgclass('system.listMethods', array(), $id);
168         $actionname = 'List of available methods';
169         break;
170       case 'execute':
171         if (!payload_is_safe($payload))
172           die("Tsk tsk tsk, please stop it or I will have to call in the cops!");
173         $msg[0] = new $msgclass($method, array(), $id);
174         // hack! build xml payload by hand
175         if ($wstype == 1)
176         {
177           $msg[0]->payload = "{\n".
178             '"method": "' . $method . "\",\n\"params\": [" .
179             $payload .
180             "\n],\n\"id\": ";
181             // fix: if user gave an empty string, use NULL, or we'll break json syntax
182             if ($id == "")
183             {
184                 $msg[0]->payload .= "null\n}";
185             }
186             else
187             {
188               if (is_numeric($id) || $id == 'false' || $id == 'true' || $id == 'null')
189               {
190                 $msg[0]->payload .= "$id\n}";
191               }
192               else
193               {
194                 $msg[0]->payload .= "\"$id\"\n}";
195               }
196             }
197         }
198         else
199           $msg[0]->payload = $msg[0]->xml_header() .
200             '<methodName>' . $method . "</methodName>\n<params>" .
201             $payload .
202             "</params>\n" . $msg[0]->xml_footer();
203         $actionname = 'Execution of method '.$method;
204         break;
205       default: // give a warning
206         $actionname = '[ERROR: unknown action] "'.$action.'"';
207     }
208
209     // Before calling execute, echo out brief description of action taken + date and time ???
210     // this gives good user feedback for long-running methods...
211     echo '<h2>'.htmlspecialchars($actionname).' on server '.htmlspecialchars($server)." ...</h2>\n";
212     flush();
213
214     $response = null;
215     // execute method(s)
216     if ($debug)
217       echo '<div class="dbginfo"><h2>Debug info:</h2>';  /// @todo use ob_start instead
218     $resp = array();
219     $mtime = explode(' ',microtime());
220     $time = (float)$mtime[0] + (float)$mtime[1];
221     foreach ($msg as $message)
222     {
223       // catch errors: for older xmlrpc libs, send does not return by ref
224       @$response =& $client->send($message, $timeout, $httpprotocol);
225       $resp[] = $response;
226       if (!$response || $response->faultCode())
227         break;
228     }
229     $mtime = explode(' ',microtime());
230     $time = (float)$mtime[0] + (float)$mtime[1] - $time;
231     if ($debug)
232       echo "</div>\n";
233
234     if ($response)
235     {
236
237     if ($response->faultCode())
238     {
239       // call failed! echo out error msg!
240       //echo '<h2>'.htmlspecialchars($actionname).' on server '.htmlspecialchars($server).'</h2>';
241       echo "<h3>$protoname call FAILED!</h3>\n";
242       echo "<p>Fault code: [" . htmlspecialchars($response->faultCode()) .
243         "] Reason: '" . htmlspecialchars($response->faultString()) . "'</p>\n";
244       echo (strftime("%d/%b/%Y:%H:%M:%S\n"));
245     }
246     else
247     {
248       // call succeeded: parse results
249       //echo '<h2>'.htmlspecialchars($actionname).' on server '.htmlspecialchars($server).'</h2>';
250       printf ("<h3>%s call(s) OK (%.2f secs.)</h3>\n", $protoname, $time);
251       echo (strftime("%d/%b/%Y:%H:%M:%S\n"));
252
253       switch ($action)
254       {
255         case 'list':
256
257         $v = $response->value();
258         if ($v->kindOf()=="array")
259         {
260           $max = $v->arraysize();
261           echo "<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n";
262           echo "<thead>\n<tr><th>Method</th><th>Description</th></tr>\n</thead>\n<tbody>\n";
263           for($i=0; $i < $max; $i++)
264           {
265             $rec = $v->arraymem($i);
266             if ($i%2) $class=' class="oddrow"'; else $class = ' class="evenrow"';
267             echo ("<tr><td$class>".htmlspecialchars($rec->scalarval())."</td><td$class><form action=\"controller.php\" method=\"get\" target=\"frmcontroller\">".
268               "<input type=\"hidden\" name=\"host\" value=\"".htmlspecialchars($host)."\" />".
269               "<input type=\"hidden\" name=\"port\" value=\"".htmlspecialchars($port)."\" />".
270               "<input type=\"hidden\" name=\"path\" value=\"".htmlspecialchars($path)."\" />".
271               "<input type=\"hidden\" name=\"id\" value=\"".htmlspecialchars($id)."\" />".
272               "<input type=\"hidden\" name=\"debug\" value=\"$debug\" />".
273               "<input type=\"hidden\" name=\"username\" value=\"".htmlspecialchars($username)."\" />".
274               "<input type=\"hidden\" name=\"password\" value=\"".htmlspecialchars($password)."\" />".
275               "<input type=\"hidden\" name=\"authtype\" value=\"$authtype\" />".
276               "<input type=\"hidden\" name=\"verifyhost\" value=\"$verifyhost\" />".
277               "<input type=\"hidden\" name=\"verifypeer\" value=\"$verifypeer\" />".
278               "<input type=\"hidden\" name=\"cainfo\" value=\"".htmlspecialchars($cainfo)."\" />".
279               "<input type=\"hidden\" name=\"proxy\" value=\"".htmlspecialchars($proxy)."\" />".
280               "<input type=\"hidden\" name=\"proxyuser\" value=\"".htmlspecialchars($proxyuser)."\" />".
281               "<input type=\"hidden\" name=\"proxypwd\" value=\"".htmlspecialchars($proxypwd)."\" />".
282               "<input type=\"hidden\" name=\"responsecompression\" value=\"$responsecompression\" />".
283               "<input type=\"hidden\" name=\"requestcompression\" value=\"$requestcompression\" />".
284               "<input type=\"hidden\" name=\"clientcookies\" value=\"".htmlspecialchars($clientcookies)."\" />".
285               "<input type=\"hidden\" name=\"protocol\" value=\"$protocol\" />".
286               "<input type=\"hidden\" name=\"timeout\" value=\"".htmlspecialchars($timeout)."\" />".
287               "<input type=\"hidden\" name=\"method\" value=\"".$rec->scalarval()."\" />".
288               "<input type=\"hidden\" name=\"wstype\" value=\"$wstype\" />".
289               "<input type=\"hidden\" name=\"action\" value=\"describe\" />".
290               "<input type=\"hidden\" name=\"run\" value=\"now\" />".
291               "<input type=\"submit\" value=\"Describe\" /></form></td>");
292             //echo("</tr>\n");
293
294             // generate lo scheletro per il method payload per eventuali test
295             //$methodpayload="<methodCall>\n<methodName>".$rec->scalarval()."</methodName>\n<params>\n<param><value></value></param>\n</params>\n</methodCall>";
296
297             /*echo ("<form action=\"{$_SERVER['PHP_SELF']}\" method=\"get\"><td>".
298               "<input type=\"hidden\" name=\"host\" value=\"$host\" />".
299               "<input type=\"hidden\" name=\"port\" value=\"$port\" />".
300               "<input type=\"hidden\" name=\"path\" value=\"$path\" />".
301               "<input type=\"hidden\" name=\"method\" value=\"".$rec->scalarval()."\" />".
302               "<input type=\"hidden\" name=\"methodpayload\" value=\"$payload\" />".
303               "<input type=\"hidden\" name=\"action\" value=\"execute\" />".
304               "<input type=\"submit\" value=\"Test\" /></td></form>");*/
305             echo("</tr>\n");
306           }
307           echo "</tbody>\n</table>";
308         }
309           break;
310
311         case 'describe':
312
313         $r1 = $resp[0]->value();
314         $r2 = $resp[1]->value();
315
316         echo "<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n";
317         echo "<thead>\n<tr><th>Method</th><th>".htmlspecialchars($method)."</th><th>&nbsp;</th><th>&nbsp;</th></tr>\n</thead>\n<tbody>\n";
318         $desc = htmlspecialchars($r1->scalarval());
319         if ($desc == "")
320           $desc = "-";
321         echo "<tr><td class=\"evenrow\">Description</td><td colspan=\"3\" class=\"evenrow\">$desc</td></tr>\n";
322         $payload="";
323         $alt_payload="";
324         if ($r2->kindOf()!="array")
325           echo "<tr><td class=\"oddrow\">Signature</td><td class=\"oddrow\">Unknown</td><td class=\"oddrow\">&nbsp;</td></tr>\n";
326         else
327         {
328           for($i=0; $i < $r2->arraysize(); $i++)
329           {
330             if ($i+1%2) $class=' class="oddrow"'; else $class = ' class="evenrow"';
331             echo "<tr><td$class>Signature&nbsp;".($i+1)."</td><td$class>";
332             $x = $r2->arraymem($i);
333             if ($x->kindOf()=="array")
334             {
335               $ret = $x->arraymem(0);
336               echo "<code>OUT:&nbsp;" . htmlspecialchars($ret->scalarval()) . "<br />IN: (";
337               if ($x->arraysize() > 1)
338               {
339                 for($k = 1; $k < $x->arraysize(); $k++)
340                 {
341                   $y = $x->arraymem($k);
342                   echo $y->scalarval();
343                   if ($wstype != 1)
344                   {
345                     $payload = $payload . '<param><value><'.htmlspecialchars($y->scalarval()).'></'.htmlspecialchars($y->scalarval())."></value></param>\n";
346                   }
347                   $alt_payload .= $y->scalarval();
348                   if ($k < $x->arraysize()-1)
349                   {
350                     $alt_payload .= ';';
351                     echo ", ";
352                   }
353                 }
354               }
355               echo ")</code>";
356             }
357             else
358             {
359               echo 'Unknown';
360             }
361             echo '</td>';
362             //bottone per testare questo metodo
363             //$payload="<methodCall>\n<methodName>$method</methodName>\n<params>\n$payload</params>\n</methodCall>";
364             echo "<td$class><form action=\"controller.php\" target=\"frmcontroller\" method=\"get\">".
365             "<input type=\"hidden\" name=\"host\" value=\"".htmlspecialchars($host)."\" />".
366             "<input type=\"hidden\" name=\"port\" value=\"".htmlspecialchars($port)."\" />".
367             "<input type=\"hidden\" name=\"path\" value=\"".htmlspecialchars($path)."\" />".
368             "<input type=\"hidden\" name=\"id\" value=\"".htmlspecialchars($id)."\" />".
369             "<input type=\"hidden\" name=\"debug\" value=\"$debug\" />".
370             "<input type=\"hidden\" name=\"username\" value=\"".htmlspecialchars($username)."\" />".
371             "<input type=\"hidden\" name=\"password\" value=\"".htmlspecialchars($password)."\" />".
372             "<input type=\"hidden\" name=\"authtype\" value=\"$authtype\" />".
373             "<input type=\"hidden\" name=\"verifyhost\" value=\"$verifyhost\" />".
374             "<input type=\"hidden\" name=\"verifypeer\" value=\"$verifypeer\" />".
375             "<input type=\"hidden\" name=\"cainfo\" value=\"".htmlspecialchars($cainfo)."\" />".
376             "<input type=\"hidden\" name=\"proxy\" value=\"".htmlspecialchars($proxy)."\" />".
377             "<input type=\"hidden\" name=\"proxyuser\" value=\"".htmlspecialchars($proxyuser)."\" />".
378             "<input type=\"hidden\" name=\"proxypwd\" value=\"".htmlspecialchars($proxypwd)."\" />".
379             "<input type=\"hidden\" name=\"responsecompression\" value=\"$responsecompression\" />".
380             "<input type=\"hidden\" name=\"requestcompression\" value=\"$requestcompression\" />".
381             "<input type=\"hidden\" name=\"clientcookies\" value=\"".htmlspecialchars($clientcookies)."\" />".
382             "<input type=\"hidden\" name=\"protocol\" value=\"$protocol\" />".
383             "<input type=\"hidden\" name=\"timeout\" value=\"".htmlspecialchars($timeout)."\" />".
384             "<input type=\"hidden\" name=\"method\" value=\"".htmlspecialchars($method)."\" />".
385             "<input type=\"hidden\" name=\"methodpayload\" value=\"".htmlspecialchars($payload)."\" />".
386             "<input type=\"hidden\" name=\"altmethodpayload\" value=\"".htmlspecialchars($alt_payload)."\" />".
387             "<input type=\"hidden\" name=\"wstype\" value=\"$wstype\" />".
388             "<input type=\"hidden\" name=\"action\" value=\"execute\" />";
389             if ($wstype != 1)
390               echo "<input type=\"submit\" value=\"Load method synopsis\" />";
391             echo "</form></td>\n";
392
393             echo "<td$class><form action=\"controller.php\" target=\"frmcontroller\" method=\"get\">".
394             "<input type=\"hidden\" name=\"host\" value=\"".htmlspecialchars($host)."\" />".
395             "<input type=\"hidden\" name=\"port\" value=\"".htmlspecialchars($port)."\" />".
396             "<input type=\"hidden\" name=\"path\" value=\"".htmlspecialchars($path)."\" />".
397             "<input type=\"hidden\" name=\"id\" value=\"".htmlspecialchars($id)."\" />".
398             "<input type=\"hidden\" name=\"debug\" value=\"$debug\" />".
399             "<input type=\"hidden\" name=\"username\" value=\"".htmlspecialchars($username)."\" />".
400             "<input type=\"hidden\" name=\"password\" value=\"".htmlspecialchars($password)."\" />".
401             "<input type=\"hidden\" name=\"authtype\" value=\"$authtype\" />".
402             "<input type=\"hidden\" name=\"verifyhost\" value=\"$verifyhost\" />".
403             "<input type=\"hidden\" name=\"verifypeer\" value=\"$verifypeer\" />".
404             "<input type=\"hidden\" name=\"cainfo\" value=\"".htmlspecialchars($cainfo)."\" />".
405             "<input type=\"hidden\" name=\"proxy\" value=\"".htmlspecialchars($proxy)."\" />".
406             "<input type=\"hidden\" name=\"proxyuser\" value=\"".htmlspecialchars($proxyuser)."\" />".
407             "<input type=\"hidden\" name=\"proxypwd\" value=\"".htmlspecialchars($proxypwd)."\" />".
408             "<input type=\"hidden\" name=\"responsecompression\" value=\"$responsecompression\" />".
409             "<input type=\"hidden\" name=\"requestcompression\" value=\"$requestcompression\" />".
410             "<input type=\"hidden\" name=\"clientcookies\" value=\"".htmlspecialchars($clientcookies)."\" />".
411             "<input type=\"hidden\" name=\"protocol\" value=\"$protocol\" />".
412             "<input type=\"hidden\" name=\"timeout\" value=\"".htmlspecialchars($timeout)."\" />".
413             "<input type=\"hidden\" name=\"method\" value=\"".htmlspecialchars($method)."\" />".
414             "<input type=\"hidden\" name=\"methodsig\" value=\"".$i."\" />".
415             "<input type=\"hidden\" name=\"methodpayload\" value=\"".htmlspecialchars($payload)."\" />".
416             "<input type=\"hidden\" name=\"altmethodpayload\" value=\"".htmlspecialchars($alt_payload)."\" />".
417             "<input type=\"hidden\" name=\"wstype\" value=\"$wstype\" />".
418             "<input type=\"hidden\" name=\"run\" value=\"now\" />".
419             "<input type=\"hidden\" name=\"action\" value=\"wrap\" />".
420             "<input type=\"submit\" value=\"Generate method call stub code\" />";
421             echo "</form></td></tr>\n";
422
423           }
424         }
425         echo "</tbody>\n</table>";
426
427           break;
428
429         case 'wrap':
430           $r1 = $resp[0]->value();
431           $r2 = $resp[1]->value();
432           if ($r2->kindOf()!="array" || $r2->arraysize() <= $methodsig)
433             echo "Error: signature unknown\n";
434           else
435           {
436           $mdesc = $r1->scalarval();
437           $msig = php_xmlrpc_decode($r2);
438           $msig = $msig[$methodsig];
439           $proto = $protocol == 2 ? 'https' : $protocol == 1 ? 'http11' : '';
440           if ($proxy  == '' && $username == '' && !$requestcompression && !$responsecompression &&
441             $clientcookies == '')
442           {
443             $opts = 0; // simple client copy in stub code
444           }
445           else
446           {
447             $opts = 1; // complete client copy in stub code
448           }
449           if ($wstype == 1)
450           {
451             $prefix = 'jsonrpc';
452           }
453           else
454           {
455             $prefix = 'xmlrpc';
456           }
457           //$code = wrap_xmlrpc_method($client, $method, $methodsig, 0, $proto, '', $opts);
458           $code = build_remote_method_wrapper_code($client, $method, str_replace('.', '_', $prefix.'_'.$method), $msig, $mdesc, $timeout, $proto, $opts, $prefix);
459           //if ($code)
460           //{
461               echo "<div id=\"phpcode\">\n";
462             highlight_string("<?php\n".$code['docstring'].$code['source'].'?>');
463             echo "\n</div>";
464           //}
465           //else
466           //{
467           //  echo 'Error while building php code stub...';
468           }
469
470           break;
471
472         case 'execute':
473           echo '<div id="response"><h2>Response:</h2>'.htmlspecialchars($response->serialize()).'</div>';
474           break;
475
476         default: // give a warning
477       }
478     } // if !$response->faultCode()
479     } // if $response
480   }
481   else
482   {
483     // no action taken yet: give some instructions on debugger usage
484 ?>
485
486 <h3>Instructions on usage of the debugger:</h3>
487 <ol>
488 <li>Run a 'list available methods' action against desired server</li>
489 <li>If list of methods appears, click on 'describe method' for desired method</li>
490 <li>To run method: click on 'load method synopsis' for desired method. This will load a skeleton for method call parameters in the form above. Complete all xmlrpc values with appropriate data and click 'Execute'</li>
491 </ol>
492 <?php
493   if (!extension_loaded('curl'))
494   {
495       echo "<p class=\"evidence\">You will need to enable the CURL extension to use the HTTPS and HTTP 1.1 transports</p>\n";
496   }
497 ?>
498
499 <h3>Example:</h3>
500 <p>
501 Server Address: phpxmlrpc.sourceforge.net<br/>
502 Path: /server.php
503 </p>
504
505 <h3>Notice:</h3>
506 <p>all usernames and passwords entered on the above form will be written to the web server logs of this server. Use with care.</p>
507
508 <h3>Changelog</h3>
509 <ul>
510 <li>2007-02-20: add visual editor for method payload; allow strings, bools as jsonrpc msg id</li>
511 <li>2006-06-26: support building php code stub for calling remote methods</li>
512 <li>2006-05-25: better support for long running queries; check for no-curl installs</li>
513 <li>2006-05-02: added support for JSON-RPC. Note that many interesting json-rpc features are not implemented yet, such as notifications or multicall.</li>
514 <li>2006-04-22: added option for setting custom CA certs to verify peer with in SSLmode</li>
515 <li>2006-03-05: added option for setting Basic/Digest/NTLM auth type</li>
516 <li>2006-01-18: added option echoing to screen xmlrpc request before sending it ('More' debug)</li>
517 <li>2005-10-01: added option for setting cookies to be sent to server</li>
518 <li>2005-08-07: added switches for compression of requests and responses and http 1.1</li>
519 <li>2005-06-27: fixed possible security breach in parsing malformed xml</li>
520 <li>2005-06-24: fixed error with calling methods having parameters...</li>
521 </ul>
522 <?php
523   }
524 ?>
525 </body>
526 </html>