]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/XmlRpcServer.php
Fixed so that wiki.listLinks() returns external as well as internal links.
[SourceForge/phpwiki.git] / lib / XmlRpcServer.php
1 <?php 
2 // $Id: XmlRpcServer.php,v 1.2 2002-09-15 05:52:23 dairiki 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 //        Remove all warnings from xmlrpc.inc 
51 //        Return list of external links in listLinks
52  
53
54 // Intercept GET requests from confused users.  Only POST is allowed here!
55 // There is some indication that $HTTP_SERVER_VARS is deprecated in php > 4.1.0
56 // in favour of $_Server, but as far as I know, it still works.
57 if ($GLOBALS['HTTP_SERVER_VARS']['REQUEST_METHOD'] != "POST")  
58 {
59     die('This is the address of the XML-RPC interface.' .
60         '  You must use XML-RPC calls to access information here');
61 }
62
63 // Include the php XML-RPC library
64
65 // All these global declarations make it so that this file
66 // (XmlRpcServer.php) can be included within a function body
67 // (not in global scope), and things will still work....
68
69 global $xmlrpcI4, $xmlrpcInt, $xmlrpcBoolean, $xmlrpcDouble, $xmlrpcString;
70 global $xmlrpcDateTime, $xmlrpcBase64, $xmlrpcArray, $xmlrpcStruct;
71 global $xmlrpcTypes;
72 global $xmlEntities;
73 global $xmlrpcerr, $xmlrpcstr;
74 global $xmlrpc_defencoding;
75 global $xmlrpcName, $xmlrpcVersion;
76 global $xmlrpcerruser, $xmlrpcerrxml;
77 global $xmlrpc_backslash;
78 global $_xh;
79 include_once("lib/XMLRPC/xmlrpc.inc");
80
81 global $_xmlrpcs_dmap;
82 global $_xmlrpcs_debug;
83 include_once("lib/XMLRPC/xmlrpcs.inc");
84
85 //  API version
86 define ("WIKI_XMLRPC_VERSION", 1);
87
88 /**
89  * Helper function:  Looks up a page revision (most recent by default) in the wiki database
90  * 
91  * @param xmlrpcmsg $params :  string pagename [int version]
92  * @return WikiDB _PageRevision object, or false if no such page
93  */
94
95 function _getPageRevision ($params)
96 {
97     global $request;
98     $ParamPageName = $params->getParam(0);
99     $ParamVersion = $params->getParam(1);
100     $pagename = short_string_decode($ParamPageName->scalarval());
101     $version =  ($ParamVersion) ? ($ParamVersion->scalarval()):(0);
102     // FIXME:  test for version <=0 ??
103     $dbh = $request->getDbh();
104     if ($dbh->isWikiPage($pagename)) {
105         $page = $dbh->getPage($pagename);
106         if (!$version) {
107             $revision = $page->getCurrentRevision();
108         } else {
109             $revision = $page->getRevision($version);
110         } 
111         return $revision;
112     } 
113     return false;
114
115
116 /*
117  * Helper functions for encoding/decoding strings.
118  *
119  * According to WikiRPC spec, all returned strings take one of either
120  * two forms.  Short strings (page names, and authors) are converted to
121  * UTF-8, then rawurlencode()d, and returned as XML-RPC <code>strings</code>.
122  * Long strings (page content) are converted to UTF-8 then returned as
123  * XML-RPC <code>base64</code> binary objects.
124  */
125
126 /**
127  * Urlencode ASCII control characters.
128  *
129  * (And control characters...)
130  *
131  * @param string $str
132  * @return string
133  * @see urlencode
134  */
135 function UrlencodeControlCharacters($str) {
136     return preg_replace('/([\x00-\x20])/e', "urlencode('\\1')", $str);
137 }
138
139 /**
140  * Convert a short string (page name, author) to xmlrpcval.
141  */
142 function short_string ($str) {
143     return new xmlrpcval(UrlencodeControlCharacters(utf8_encode($str)), 'string');
144 }
145
146 /**
147  * Convert a large string (page content) to xmlrpcval.
148  */
149 function long_string ($str) {
150     return new xmlrpcval(utf8_encode($str), 'base64');
151 }
152
153 /**
154  * Decode a short string (e.g. page name)
155  */
156 function short_string_decode ($str) {
157     return utf8_decode(urldecode($str));
158 }
159
160 /**
161  * Get an xmlrpc "No such page" error message
162  */
163 function NoSuchPage () 
164 {
165     global $xmlrpcerruser;
166     return new xmlrpcresp(0, $xmlrpcerruser + 1, "No such page");
167 }
168
169
170 // ****************************************************************************
171 // Main API functions follow
172 // ****************************************************************************
173 global $wiki_dmap;
174
175 /**
176  * int getRPCVersionSupported(): Returns 1 for this version of the API 
177  */
178 $wiki_dmap['getRPCVersionSupported']
179 = array('signature'     => array(array($xmlrpcInt)),
180         'documentation' => 'Get the version of the wiki API',
181         'function'      => 'getRPCVersionSupported');
182
183 // The function must be a function in the global scope which services the XML-RPC
184 // method.
185 function getRPCVersionSupported($params)
186 {
187     return new xmlrpcresp(new xmlrpcval(WIKI_XMLRPC_VERSION, "int"));
188 }
189
190 /**
191  * array getRecentChanges(Date timestamp) : Get list of changed pages since 
192  * timestamp, which should be in UTC. The result is an array, where each element
193  * is a struct: 
194  *     name (string) : Name of the page. The name is UTF-8 with URL encoding to make it ASCII. 
195  *     lastModified (date) : Date of last modification, in UTC. 
196  *     author (string) : Name of the author (if available). Again, name is UTF-8 with URL encoding. 
197  *         version (int) : Current version. 
198  * A page MAY be specified multiple times. A page MAY NOT be specified multiple 
199  * times with the same modification date.
200  */
201 $wiki_dmap['getRecentChanges']
202 = array('signature'     => array(array($xmlrpcArray, $xmlrpcDateTime)),
203         'documentation' => 'Get a list of changed pages since [timestamp]',
204         'function'      => 'getRecentChanges');
205
206 function getRecentChanges($params)
207 {
208     global $request;
209     // Get the first parameter as an ISO 8601 date.  Assume UTC
210     $encoded_date = $params->getParam(0);
211     $datetime = iso8601_decode($encoded_date->scalarval(), 1);
212     $dbh = $request->getDbh();
213     $pages = array();
214     $iterator = $dbh->mostRecent(array('since' => $datetime));
215     while ($page = $iterator->next()) {
216         // $page contains a WikiDB_PageRevision object
217         // no need to url encode $name, because it is already stored in that format ???
218         $name = short_string($page->getPageName());
219         $lastmodified = new xmlrpcval(iso8601_encode($page->get('mtime')), "dateTime.iso8601");
220         $author = short_string($page->get('author'));
221         $version = new xmlrpcval($page->getVersion(), 'int');
222
223         // Build an array of xmlrpc structs
224         $pages[] = new xmlrpcval(array('name'=>$name, 
225                                        'lastModified'=>$lastmodified,
226                                        'author'=>$author,
227                                        'version'=>$version),
228                                  'struct');
229     } 
230     return new xmlrpcresp(new xmlrpcval($pages, "array"));
231
232
233
234 /**
235  * base64 getPage( String pagename ): Get the raw Wiki text of page, latest version. 
236  * Page name must be UTF-8, with URL encoding. Returned value is a binary object,
237  * with UTF-8 encoded page data.
238  */
239 $wiki_dmap['getPage']
240 = array('signature'     => array(array($xmlrpcBase64, $xmlrpcString)),
241         'documentation' => 'Get the raw Wiki text of the current version of a page',
242         'function'      => 'getPage');
243
244 function getPage($params)
245 {
246     $revision = _getPageRevision($params);
247
248     if (! $revision)
249         return NoSuchPage();
250
251     return new xmlrpcresp(long_string($revision->getPackedContent()));
252 }
253  
254
255 /**
256  * base64 getPageVersion( String pagename, int version ): Get the raw Wiki text of page.
257  * Returns UTF-8, expects UTF-8 with URL encoding.
258  */
259 $wiki_dmap['getPageVersion']
260 = array('signature'     => array(array($xmlrpcBase64, $xmlrpcString, $xmlrpcInt)),
261         'documentation' => 'Get the raw Wiki text of a page version',
262         'function'      => 'getPageVersion');
263
264 function getPageVersion($params)
265 {
266     // error checking is done in getPage
267     return getPage($params);
268
269
270 /**
271  * base64 getPageHTML( String pagename ): Return page in rendered HTML. 
272  * Returns UTF-8, expects UTF-8 with URL encoding.
273  */
274
275 $wiki_dmap['getPageHTML']
276 = array('signature'     => array(array($xmlrpcBase64, $xmlrpcString)),
277         'documentation' => 'Get the current version of a page rendered in HTML',
278         'function'      => 'getPageHTML');
279
280 function getPageHTML($params)
281 {
282     $revision = _getPageRevision($params);
283     if (!$revision)
284         return NoSuchPage();
285     
286     include_once('lib/PageType.php');
287     $content = array(PageType($revision));
288
289     // Get rid of outer <div class="wikitext">
290     while (count($content) == 1 && isa($content[0], 'XmlContent')) {
291         if (isa($content[0], 'XmlElement') && $content[0]->getTag() != 'div')
292             break;
293         $content = $content[0]->getContent();
294     }
295
296     return new xmlrpcresp(long_string(AsXML($content)));
297
298
299 /**
300  * base64 getPageHTMLVersion( String pagename, int version ): Return page in rendered HTML, UTF-8.
301  */
302 $wiki_dmap['getPageHTMLVersion']
303 = array('signature'     => array(array($xmlrpcBase64, $xmlrpcString, $xmlrpcInt)),
304         'documentation' => 'Get a version of a page rendered in HTML',
305         'function'      => 'getPageHTMLVersion');
306
307 function getPageHTMLVersion($params)
308 {
309     return getPageHTML($params);
310
311
312 /**
313  * getAllPages(): Returns a list of all pages. The result is an array of strings.
314  */
315 $wiki_dmap['getAllPages']
316 = array('signature'     => array(array($xmlrpcArray)),
317         'documentation' => 'Returns a list of all pages as an array of strings', 
318         'function'      => 'getAllPages');
319
320 function getAllPages($params)
321 {
322     global $request;
323     $dbh = $request->getDbh();
324     $iterator = $dbh->getAllPages();
325     $pages = array();
326     while ($page = $iterator->next()) {
327         $pages[] = short_string($page->getName());
328     } 
329     return new xmlrpcresp(new xmlrpcval($pages, "array"));
330
331
332 /**
333  * struct getPageInfo( string pagename ) : returns a struct with elements: 
334  *   name (string): the canonical page name 
335  *   lastModified (date): Last modification date 
336  *   version (int): current version 
337  *       author (string): author name 
338  */
339 $wiki_dmap['getPageInfo']
340 = array('signature'     => array(array($xmlrpcStruct, $xmlrpcString)),
341         'documentation' => 'Gets info about the current version of a page',
342         'function'      => 'getPageInfo');
343
344 function getPageInfo($params)
345 {
346     $revision = _getPageRevision($params);
347     if (!$revision)
348         return NoSuchPage();
349     
350     $name = short_string($revision->getPageName());
351     $version = new xmlrpcval ($revision->getVersion(), "int");
352     $lastmodified = new xmlrpcval(iso8601_encode($revision->get('mtime'), 0),
353                                   "dateTime.iso8601");
354     $author = short_string($revision->get('author'));
355         
356     return new xmlrpcresp(new xmlrpcval(array('name' => $name, 
357                                               'lastModified' => $lastmodified,
358                                               'version' => $version, 
359                                               'author' => $author), 
360                                         "struct"));
361
362
363 /**
364  * struct getPageInfoVersion( string pagename, int version ) : returns
365  * a struct just like plain getPageInfo(), but this time for a
366  * specific version.
367  */
368 $wiki_dmap['getPageInfoVersion']
369 = array('signature'     => array(array($xmlrpcStruct, $xmlrpcString, $xmlrpcInt)),
370         'documentation' => 'Gets info about a page version',
371         'function'      => 'getPageInfoVersion');
372
373 function getPageInfoVersion($params)
374 {
375     return getPageInfo($params);
376 }
377
378  
379 /*  array listLinks( string pagename ): Lists all links for a given page. The
380  *  returned array contains structs, with the following elements: 
381  *       name (string) : The page name or URL the link is to. 
382  *       type (int) : The link type. Zero (0) for internal Wiki link,
383  *         one (1) for external link (URL - image link, whatever).
384  */
385 $wiki_dmap['listLinks']
386 = array('signature'     => array(array($xmlrpcArray, $xmlrpcString)),
387         'documentation' => 'Lists all links for a given page',
388         'function'      => 'listLinks');
389
390 function listLinks($params)
391 {
392     global $request;
393     
394     $ParamPageName = $params->getParam(0);
395     $pagename = short_string_decode($ParamPageName->scalarval());
396     $dbh = $request->getDbh();
397     if (! $dbh->isWikiPage($pagename))
398         return NoSuchPage();
399
400     $page = $dbh->getPage($pagename);
401     /*
402     $linkiterator = $page->getLinks(false);
403     $linkstruct = array();
404     while ($currentpage = $linkiterator->next()) {
405         $currentname = $currentpage->getName();
406         $name = short_string($currentname);
407         // NB no clean way to extract a list of external links yet, so
408         // only internal links returned.  ie all type 'local'.
409         $type = new xmlrpcval('local');
410
411         // Compute URL to page
412         $args = array();
413         $currentrev = $currentpage->getCurrentRevision();
414         if ($currentrev->hasDefaultContents())
415             $args['action'] = 'edit';
416
417         // FIXME: Autodetected value of VIRTUAL_PATH wrong,
418         // this make absolute URLs contstructed by WikiURL wrong.
419         // Also, if USE_PATH_INFO is false, WikiURL is wrong
420         // due to its use of SCRIPT_NAME.
421         $use_abspath = USE_PATH_INFO && ! preg_match('/RPC2.php$/', VIRTUAL_PATH);
422         $href = new xmlrpcval(WikiURL($currentname, $args, $use_abspath));
423             
424         $linkstruct[] = new xmlrpcval(array('name'=> $name,
425                                             'type'=> $type,
426                                             'href' => $href),
427                                       "struct");
428     }
429     */
430     
431     $current = $page->getCurrentRevision();
432     list ($pages, $urls) = ExtractLinks($current->getContent());
433
434     $type = new xmlrpcval("internal");
435     foreach ($pages as $wikiword) {
436         $name = short_string($wikiword);
437         $args = array();
438         if (! $dbh->isWikiPage($wikiword))
439             $args['action'] = 'edit';
440         // FIXME: see comments in above commented sections
441         // about possible screwed up absolute URLS...
442         $href = short_string(WikiURL($wikiword, $args, 'abspath'));
443         $linkstruct[] = new xmlrpcval(array('name'=> $name,
444                                             'type'=> $type,
445                                             'href' => $href),
446                                       "struct");
447     }
448         
449     $type = new xmlrpcval("external");
450     foreach ($urls as $url) {
451         $href = short_string($url);
452         $linkstruct[] = new xmlrpcval(array('name'=> $href,
453                                             'type'=> $type,
454                                             'href' => $href),
455                                       "struct");
456     }
457     
458     return new xmlrpcresp(new xmlrpcval ($linkstruct, "array"));
459
460  
461 // Construct the server instance, and set up the despatch map, which maps
462 // the XML-RPC methods onto the wiki functions
463 class XmlRpcServer extends xmlrpc_server
464 {
465     function XmlRpcServer ($request = false) {
466         global $wiki_dmap;
467         foreach ($wiki_dmap as $name => $val)
468             $dmap['wiki.' . $name] = $val;
469         
470         $this->xmlrpc_server($dmap, 0 /* delay service*/);
471     }
472
473     function service () {
474         global $ErrorManager;
475
476         $this->errbuf = '';
477         $ErrorManager->pushErrorHandler(new WikiMethodCb($this, '_errorHandler'));
478
479         xmlrpc_server::service();
480
481         $ErrorManager->popErrorHandler();
482         print $this->errbuf;
483     }
484     
485     function _errorHandler ($e) {
486         $msg = htmlspecialchars($e->asString());
487         // '--' not allowed within xml comment
488         $msg = str_replace('--', '&#45;&#45;', $msg);
489         $this->errbuf .= sprintf("<!--\n%s\n-->", $msg);
490         return true;
491     }
492 }
493
494
495 // (c-file-style: "gnu")
496 // Local Variables:
497 // mode: php
498 // tab-width: 8
499 // c-basic-offset: 4
500 // c-hanging-comment-ender-p: nil
501 // indent-tabs-mode: nil
502 // End:   
503 ?>