]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/XmlRpcServer.php
fixed problem with two different dsn and auth_dsn database backends
[SourceForge/phpwiki.git] / lib / XmlRpcServer.php
1 <?php 
2 // $Id: XmlRpcServer.php,v 1.6 2004-03-25 22:33:38 rurban Exp $
3 /* Copyright (C) 2002, Lawrence Akka <lakka@users.sourceforge.net>
4  *
5  * LICENCE
6  * =======
7  * This file is part of PhpWiki.
8  * 
9  * PhpWiki is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * PhpWiki is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  * 
19  * You should have received a copy of the GNU General Public License
20  * along with PhpWiki; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  * LIBRARY USED - POSSIBLE PROBLEMS
24  * ================================
25  * 
26  * This file provides an XML-RPC interface for PhpWiki.  It uses the XML-RPC 
27  * library for PHP by Edd Dumbill - see http://xmlrpc.usefulinc.com/php.html 
28  * for details.
29  *
30  * PHP >= 4.1.0 includes experimental support for the xmlrpc-epi c library 
31  * written by Dan Libby (see http://uk2.php.net/manual/en/ref.xmlrpc.php).  This
32  * is not compiled into PHP by default.  If it *is* compiled into your installation
33  * (ie you have --with-xmlrpc) there may well be namespace conflicts with the xml-rpc
34  * library used by this code, and you will get errors.
35  * 
36  * INTERFACE SPECIFICTION
37  * ======================
38  *  
39  * The interface specification is that discussed at 
40  * http://www.ecyrd.com/JSPWiki/Wiki.jsp?page=WikiRPCInterface
41  * 
42  * See also http://www.usemod.com/cgi-bin/mb.pl?XmlRpc
43  * 
44  * NB:  All XMLRPC methods should be prefixed with "wiki."
45  * eg  wiki.getAllPages
46  * 
47 */
48
49 // ToDo:  
50 //        Make use of the xmlrpc extension if found. Resolve namespace conflicts
51 //        Remove all warnings from xmlrpc.inc 
52 //        Return list of external links in listLinks
53  
54
55 // Intercept GET requests from confused users.  Only POST is allowed here!
56 // There is some indication that $HTTP_SERVER_VARS is deprecated in php > 4.1.0
57 // in favour of $_Server, but as far as I know, it still works.
58 if ($GLOBALS['HTTP_SERVER_VARS']['REQUEST_METHOD'] != "POST")  
59 {
60     die('This is the address of the XML-RPC interface.' .
61         '  You must use XML-RPC calls to access information here');
62 }
63
64 // Include the php XML-RPC library
65
66 // All these global declarations make it so that this file
67 // (XmlRpcServer.php) can be included within a function body
68 // (not in global scope), and things will still work....
69
70 global $xmlrpcI4, $xmlrpcInt, $xmlrpcBoolean, $xmlrpcDouble, $xmlrpcString;
71 global $xmlrpcDateTime, $xmlrpcBase64, $xmlrpcArray, $xmlrpcStruct;
72 global $xmlrpcTypes;
73 global $xmlEntities;
74 global $xmlrpcerr, $xmlrpcstr;
75 global $xmlrpc_defencoding;
76 global $xmlrpcName, $xmlrpcVersion;
77 global $xmlrpcerruser, $xmlrpcerrxml;
78 global $xmlrpc_backslash;
79 global $_xh;
80 include_once("lib/XMLRPC/xmlrpc.inc");
81
82 global $_xmlrpcs_dmap;
83 global $_xmlrpcs_debug;
84 include_once("lib/XMLRPC/xmlrpcs.inc");
85
86 //  API version
87 define ("WIKI_XMLRPC_VERSION", 1);
88
89 /**
90  * Helper function:  Looks up a page revision (most recent by default) in the wiki database
91  * 
92  * @param xmlrpcmsg $params :  string pagename [int version]
93  * @return WikiDB _PageRevision object, or false if no such page
94  */
95
96 function _getPageRevision ($params)
97 {
98     global $request;
99     $ParamPageName = $params->getParam(0);
100     $ParamVersion = $params->getParam(1);
101     $pagename = short_string_decode($ParamPageName->scalarval());
102     $version =  ($ParamVersion) ? ($ParamVersion->scalarval()):(0);
103     // FIXME:  test for version <=0 ??
104     $dbh = $request->getDbh();
105     if ($dbh->isWikiPage($pagename)) {
106         $page = $dbh->getPage($pagename);
107         if (!$version) {
108             $revision = $page->getCurrentRevision();
109         } else {
110             $revision = $page->getRevision($version);
111         } 
112         return $revision;
113     } 
114     return false;
115
116
117 /*
118  * Helper functions for encoding/decoding strings.
119  *
120  * According to WikiRPC spec, all returned strings take one of either
121  * two forms.  Short strings (page names, and authors) are converted to
122  * UTF-8, then rawurlencode()d, and returned as XML-RPC <code>strings</code>.
123  * Long strings (page content) are converted to UTF-8 then returned as
124  * XML-RPC <code>base64</code> binary objects.
125  */
126
127 /**
128  * Urlencode ASCII control characters.
129  *
130  * (And control characters...)
131  *
132  * @param string $str
133  * @return string
134  * @see urlencode
135  */
136 function UrlencodeControlCharacters($str) {
137     return preg_replace('/([\x00-\x1F])/e', "urlencode('\\1')", $str);
138 }
139
140 /**
141  * Convert a short string (page name, author) to xmlrpcval.
142  */
143 function short_string ($str) {
144     return new xmlrpcval(UrlencodeControlCharacters(utf8_encode($str)), 'string');
145 }
146
147 /**
148  * Convert a large string (page content) to xmlrpcval.
149  */
150 function long_string ($str) {
151     return new xmlrpcval(utf8_encode($str), 'base64');
152 }
153
154 /**
155  * Decode a short string (e.g. page name)
156  */
157 function short_string_decode ($str) {
158     return utf8_decode(urldecode($str));
159 }
160
161 /**
162  * Get an xmlrpc "No such page" error message
163  */
164 function NoSuchPage () 
165 {
166     global $xmlrpcerruser;
167     return new xmlrpcresp(0, $xmlrpcerruser + 1, "No such page");
168 }
169
170
171 // ****************************************************************************
172 // Main API functions follow
173 // ****************************************************************************
174 global $wiki_dmap;
175
176 /**
177  * int getRPCVersionSupported(): Returns 1 for this version of the API 
178  */
179 $wiki_dmap['getRPCVersionSupported']
180 = array('signature'     => array(array($xmlrpcInt)),
181         'documentation' => 'Get the version of the wiki API',
182         'function'      => 'getRPCVersionSupported');
183
184 // The function must be a function in the global scope which services the XML-RPC
185 // method.
186 function getRPCVersionSupported($params)
187 {
188     return new xmlrpcresp(new xmlrpcval(WIKI_XMLRPC_VERSION, "int"));
189 }
190
191 /**
192  * array getRecentChanges(Date timestamp) : Get list of changed pages since 
193  * timestamp, which should be in UTC. The result is an array, where each element
194  * is a struct: 
195  *     name (string) : Name of the page. The name is UTF-8 with URL encoding to make it ASCII. 
196  *     lastModified (date) : Date of last modification, in UTC. 
197  *     author (string) : Name of the author (if available). Again, name is UTF-8 with URL encoding. 
198  *         version (int) : Current version. 
199  * A page MAY be specified multiple times. A page MAY NOT be specified multiple 
200  * times with the same modification date.
201  */
202 $wiki_dmap['getRecentChanges']
203 = array('signature'     => array(array($xmlrpcArray, $xmlrpcDateTime)),
204         'documentation' => 'Get a list of changed pages since [timestamp]',
205         'function'      => 'getRecentChanges');
206
207 function getRecentChanges($params)
208 {
209     global $request;
210     // Get the first parameter as an ISO 8601 date.  Assume UTC
211     $encoded_date = $params->getParam(0);
212     $datetime = iso8601_decode($encoded_date->scalarval(), 1);
213     $dbh = $request->getDbh();
214     $pages = array();
215     $iterator = $dbh->mostRecent(array('since' => $datetime));
216     while ($page = $iterator->next()) {
217         // $page contains a WikiDB_PageRevision object
218         // no need to url encode $name, because it is already stored in that format ???
219         $name = short_string($page->getPageName());
220         $lastmodified = new xmlrpcval(iso8601_encode($page->get('mtime')), "dateTime.iso8601");
221         $author = short_string($page->get('author'));
222         $version = new xmlrpcval($page->getVersion(), 'int');
223
224         // Build an array of xmlrpc structs
225         $pages[] = new xmlrpcval(array('name'=>$name, 
226                                        'lastModified'=>$lastmodified,
227                                        'author'=>$author,
228                                        'version'=>$version),
229                                  'struct');
230     } 
231     return new xmlrpcresp(new xmlrpcval($pages, "array"));
232
233
234
235 /**
236  * base64 getPage( String pagename ): Get the raw Wiki text of page, latest version. 
237  * Page name must be UTF-8, with URL encoding. Returned value is a binary object,
238  * with UTF-8 encoded page data.
239  */
240 $wiki_dmap['getPage']
241 = array('signature'     => array(array($xmlrpcBase64, $xmlrpcString)),
242         'documentation' => 'Get the raw Wiki text of the current version of a page',
243         'function'      => 'getPage');
244
245 function getPage($params)
246 {
247     $revision = _getPageRevision($params);
248
249     if (! $revision)
250         return NoSuchPage();
251
252     return new xmlrpcresp(long_string($revision->getPackedContent()));
253 }
254  
255
256 /**
257  * base64 getPageVersion( String pagename, int version ): Get the raw Wiki text of page.
258  * Returns UTF-8, expects UTF-8 with URL encoding.
259  */
260 $wiki_dmap['getPageVersion']
261 = array('signature'     => array(array($xmlrpcBase64, $xmlrpcString, $xmlrpcInt)),
262         'documentation' => 'Get the raw Wiki text of a page version',
263         'function'      => 'getPageVersion');
264
265 function getPageVersion($params)
266 {
267     // error checking is done in getPage
268     return getPage($params);
269
270
271 /**
272  * base64 getPageHTML( String pagename ): Return page in rendered HTML. 
273  * Returns UTF-8, expects UTF-8 with URL encoding.
274  */
275
276 $wiki_dmap['getPageHTML']
277 = array('signature'     => array(array($xmlrpcBase64, $xmlrpcString)),
278         'documentation' => 'Get the current version of a page rendered in HTML',
279         'function'      => 'getPageHTML');
280
281 function getPageHTML($params)
282 {
283     $revision = _getPageRevision($params);
284     if (!$revision)
285         return NoSuchPage();
286     
287     $content = $revision->getTransformedContent();
288     $html = $content->asXML();
289     // HACK: Get rid of outer <div class="wikitext">
290     if (preg_match('/^\s*<div class="wikitext">/', $html, $m1)
291         && preg_match('@</div>\s*$@', $html, $m2)) {
292         $html = substr($html, strlen($m1[0]), -strlen($m2[0]));
293     }
294
295     return new xmlrpcresp(long_string($html));
296
297
298 /**
299  * base64 getPageHTMLVersion( String pagename, int version ): Return page in rendered HTML, UTF-8.
300  */
301 $wiki_dmap['getPageHTMLVersion']
302 = array('signature'     => array(array($xmlrpcBase64, $xmlrpcString, $xmlrpcInt)),
303         'documentation' => 'Get a version of a page rendered in HTML',
304         'function'      => 'getPageHTMLVersion');
305
306 function getPageHTMLVersion($params)
307 {
308     return getPageHTML($params);
309
310
311 /**
312  * getAllPages(): Returns a list of all pages. The result is an array of strings.
313  */
314 $wiki_dmap['getAllPages']
315 = array('signature'     => array(array($xmlrpcArray)),
316         'documentation' => 'Returns a list of all pages as an array of strings', 
317         'function'      => 'getAllPages');
318
319 function getAllPages($params)
320 {
321     global $request;
322     $dbh = $request->getDbh();
323     $iterator = $dbh->getAllPages();
324     $pages = array();
325     while ($page = $iterator->next()) {
326         $pages[] = short_string($page->getName());
327     } 
328     return new xmlrpcresp(new xmlrpcval($pages, "array"));
329
330
331 /**
332  * struct getPageInfo( string pagename ) : returns a struct with elements: 
333  *   name (string): the canonical page name 
334  *   lastModified (date): Last modification date 
335  *   version (int): current version 
336  *       author (string): author name 
337  */
338 $wiki_dmap['getPageInfo']
339 = array('signature'     => array(array($xmlrpcStruct, $xmlrpcString)),
340         'documentation' => 'Gets info about the current version of a page',
341         'function'      => 'getPageInfo');
342
343 function getPageInfo($params)
344 {
345     $revision = _getPageRevision($params);
346     if (!$revision)
347         return NoSuchPage();
348     
349     $name = short_string($revision->getPageName());
350     $version = new xmlrpcval ($revision->getVersion(), "int");
351     $lastmodified = new xmlrpcval(iso8601_encode($revision->get('mtime'), 0),
352                                   "dateTime.iso8601");
353     $author = short_string($revision->get('author'));
354         
355     return new xmlrpcresp(new xmlrpcval(array('name' => $name, 
356                                               'lastModified' => $lastmodified,
357                                               'version' => $version, 
358                                               'author' => $author), 
359                                         "struct"));
360
361
362 /**
363  * struct getPageInfoVersion( string pagename, int version ) : returns
364  * a struct just like plain getPageInfo(), but this time for a
365  * specific version.
366  */
367 $wiki_dmap['getPageInfoVersion']
368 = array('signature'     => array(array($xmlrpcStruct, $xmlrpcString, $xmlrpcInt)),
369         'documentation' => 'Gets info about a page version',
370         'function'      => 'getPageInfoVersion');
371
372 function getPageInfoVersion($params)
373 {
374     return getPageInfo($params);
375 }
376
377  
378 /*  array listLinks( string pagename ): Lists all links for a given page. The
379  *  returned array contains structs, with the following elements: 
380  *       name (string) : The page name or URL the link is to. 
381  *       type (int) : The link type. Zero (0) for internal Wiki link,
382  *         one (1) for external link (URL - image link, whatever).
383  */
384 $wiki_dmap['listLinks']
385 = array('signature'     => array(array($xmlrpcArray, $xmlrpcString)),
386         'documentation' => 'Lists all links for a given page',
387         'function'      => 'listLinks');
388
389 function listLinks($params)
390 {
391     global $request;
392     
393     $ParamPageName = $params->getParam(0);
394     $pagename = short_string_decode($ParamPageName->scalarval());
395     $dbh = $request->getDbh();
396     if (! $dbh->isWikiPage($pagename))
397         return NoSuchPage();
398
399     $page = $dbh->getPage($pagename);
400     /*
401     $linkiterator = $page->getLinks(false);
402     $linkstruct = array();
403     while ($currentpage = $linkiterator->next()) {
404         $currentname = $currentpage->getName();
405         $name = short_string($currentname);
406         // NB no clean way to extract a list of external links yet, so
407         // only internal links returned.  ie all type 'local'.
408         $type = new xmlrpcval('local');
409
410         // Compute URL to page
411         $args = array();
412         $currentrev = $currentpage->getCurrentRevision();
413         if ($currentrev->hasDefaultContents())
414             $args['action'] = 'edit';
415
416         // FIXME: Autodetected value of VIRTUAL_PATH wrong,
417         // this make absolute URLs contstructed by WikiURL wrong.
418         // Also, if USE_PATH_INFO is false, WikiURL is wrong
419         // due to its use of SCRIPT_NAME.
420         $use_abspath = USE_PATH_INFO && ! preg_match('/RPC2.php$/', VIRTUAL_PATH);
421         $href = new xmlrpcval(WikiURL($currentname, $args, $use_abspath));
422             
423         $linkstruct[] = new xmlrpcval(array('name'=> $name,
424                                             'type'=> $type,
425                                             'href' => $href),
426                                       "struct");
427     }
428     */
429     
430     $current = $page->getCurrentRevision();
431     $content = $current->getTransformedContent();
432     $links = $content->getLinkInfo();
433
434     foreach ($links as $link) {
435         // We used to give an href for unknown pages that
436         // included action=edit.  I think that's probably the
437         // wrong thing to do.
438         $linkstruct[] = new xmlrpcval(array('name'=> short_string($link->page),
439                                             'type'=> new xmlrpcval($link->type),
440                                             'href' => short_string($link->href)),
441                                       "struct");
442     }
443         
444     return new xmlrpcresp(new xmlrpcval ($linkstruct, "array"));
445
446  
447 // Construct the server instance, and set up the despatch map, which maps
448 // the XML-RPC methods onto the wiki functions
449 class XmlRpcServer extends xmlrpc_server
450 {
451     function XmlRpcServer ($request = false) {
452         global $wiki_dmap;
453         foreach ($wiki_dmap as $name => $val)
454             $dmap['wiki.' . $name] = $val;
455         
456         $this->xmlrpc_server($dmap, 0 /* delay service*/);
457     }
458
459     function service () {
460         global $ErrorManager;
461
462         $ErrorManager->pushErrorHandler(new WikiMethodCb($this, '_errorHandler'));
463         xmlrpc_server::service();
464         $ErrorManager->popErrorHandler();
465     }
466     
467     function _errorHandler ($e) {
468         $msg = htmlspecialchars($e->asString());
469         // '--' not allowed within xml comment
470         $msg = str_replace('--', '&#45;&#45;', $msg);
471         xmlrpc_debugmsg($msg);
472         return true;
473     }
474 }
475
476
477 // (c-file-style: "gnu")
478 // Local Variables:
479 // mode: php
480 // tab-width: 8
481 // c-basic-offset: 4
482 // c-hanging-comment-ender-p: nil
483 // indent-tabs-mode: nil
484 // End:   
485 ?>