root/tags/1.5.1/xmlrpc.php

Revision 1218, 66.8 kB (checked in by donncha, 8 months ago)

Merged with WordPress? 2.5, unstable, only for testing

  • Property svn:eol-style set to native
Line 
1 <?php
2
3 define('XMLRPC_REQUEST', true);
4
5 // Some browser-embedded clients send cookies. We don't want them.
6 $_COOKIE = array();
7
8 // A bug in PHP < 5.2.2 makes $HTTP_RAW_POST_DATA not set by default,
9 // but we can do it ourself.
10 if ( !isset( $HTTP_RAW_POST_DATA ) ) {
11     $HTTP_RAW_POST_DATA = file_get_contents( 'php://input' );
12 }
13
14 # fix for mozBlog and other cases where '<?xml' isn't on the very first line
15 if ( isset($HTTP_RAW_POST_DATA) )
16     $HTTP_RAW_POST_DATA = trim($HTTP_RAW_POST_DATA);
17
18 include('./wp-config.php');
19
20 if ( isset( $_GET['rsd'] ) ) { // http://archipelago.phrasewise.com/rsd
21 header('Content-Type: text/xml; charset=' . get_option('blog_charset'), true);
22 ?>
23 <?php echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>'; ?>
24 <rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">
25   <service>
26     <engineName>WordPress</engineName>
27     <engineLink>http://wordpress.org/</engineLink>
28     <homePageLink><?php bloginfo_rss('url') ?></homePageLink>
29     <apis>
30       <api name="WordPress" blogID="1" preferred="true" apiLink="<?php bloginfo_rss('wpurl') ?>/xmlrpc.php" />
31       <api name="Movable Type" blogID="1" preferred="false" apiLink="<?php bloginfo_rss('wpurl') ?>/xmlrpc.php" />
32       <api name="MetaWeblog" blogID="1" preferred="false" apiLink="<?php bloginfo_rss('wpurl') ?>/xmlrpc.php" />
33       <api name="Blogger" blogID="1" preferred="false" apiLink="<?php bloginfo_rss('wpurl') ?>/xmlrpc.php" />
34       <api name="Atom" blogID="" preferred="false" apiLink="<?php echo apply_filters('atom_service_url', (get_bloginfo('url')."/wp-app.php/service"))?>" />
35     </apis>
36   </service>
37 </rsd>
38 <?php
39 exit;
40 }
41
42 include_once(ABSPATH . 'wp-admin/includes/admin.php');
43 include_once(ABSPATH . WPINC . '/class-IXR.php');
44
45 // Turn off all warnings and errors.
46 // error_reporting(0);
47
48 $post_default_title = ""; // posts submitted via the xmlrpc interface get that title
49
50 $xmlrpc_logging = 0;
51
52 function logIO($io,$msg) {
53     global $xmlrpc_logging;
54     if ($xmlrpc_logging) {
55         $fp = fopen("../xmlrpc.log","a+");
56         $date = gmdate("Y-m-d H:i:s ");
57         $iot = ($io == "I") ? " Input: " : " Output: ";
58         fwrite($fp, "\n\n".$date.$iot.$msg);
59         fclose($fp);
60     }
61     return true;
62     }
63
64 function starify($string) {
65     $i = strlen($string);
66     return str_repeat('*', $i);
67 }
68
69 if ( isset($HTTP_RAW_POST_DATA) )
70   logIO("I", $HTTP_RAW_POST_DATA);
71
72 class wp_xmlrpc_server extends IXR_Server {
73
74     function wp_xmlrpc_server() {
75         $this->methods = array(
76             // WordPress API
77             'wp.getPage'            => 'this:wp_getPage',
78             'wp.getPages'            => 'this:wp_getPages',
79             'wp.newPage'            => 'this:wp_newPage',
80             'wp.deletePage'            => 'this:wp_deletePage',
81             'wp.editPage'            => 'this:wp_editPage',
82             'wp.getPageList'        => 'this:wp_getPageList',
83             'wp.getAuthors'            => 'this:wp_getAuthors',
84             'wp.getCategories'        => 'this:mw_getCategories',        // Alias
85             'wp.newCategory'        => 'this:wp_newCategory',
86             'wp.deleteCategory'        => 'this:wp_deleteCategory',
87             'wp.suggestCategories'    => 'this:wp_suggestCategories',
88             'wp.uploadFile'            => 'this:mw_newMediaObject',    // Alias
89             'wp.getCommentCount'    => 'this:wp_getCommentCount',
90             'wp.getPostStatusList'    => 'this:wp_getPostStatusList',
91             'wp.getPageStatusList'    => 'this:wp_getPageStatusList',
92
93             // Blogger API
94             'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',
95             'blogger.getUserInfo' => 'this:blogger_getUserInfo',
96             'blogger.getPost' => 'this:blogger_getPost',
97             'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',
98             'blogger.getTemplate' => 'this:blogger_getTemplate',
99             'blogger.setTemplate' => 'this:blogger_setTemplate',
100             'blogger.newPost' => 'this:blogger_newPost',
101             'blogger.editPost' => 'this:blogger_editPost',
102             'blogger.deletePost' => 'this:blogger_deletePost',
103
104             // MetaWeblog API (with MT extensions to structs)
105             'metaWeblog.newPost' => 'this:mw_newPost',
106             'metaWeblog.editPost' => 'this:mw_editPost',
107             'metaWeblog.getPost' => 'this:mw_getPost',
108             'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts',
109             'metaWeblog.getCategories' => 'this:mw_getCategories',
110             'metaWeblog.newMediaObject' => 'this:mw_newMediaObject',
111
112             // MetaWeblog API aliases for Blogger API
113             // see http://www.xmlrpc.com/stories/storyReader$2460
114             'metaWeblog.deletePost' => 'this:blogger_deletePost',
115             'metaWeblog.getTemplate' => 'this:blogger_getTemplate',
116             'metaWeblog.setTemplate' => 'this:blogger_setTemplate',
117             'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',
118
119             // MovableType API
120             'mt.getCategoryList' => 'this:mt_getCategoryList',
121             'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles',
122             'mt.getPostCategories' => 'this:mt_getPostCategories',
123             'mt.setPostCategories' => 'this:mt_setPostCategories',
124             'mt.supportedMethods' => 'this:mt_supportedMethods',
125             'mt.supportedTextFilters' => 'this:mt_supportedTextFilters',
126             'mt.getTrackbackPings' => 'this:mt_getTrackbackPings',
127             'mt.publishPost' => 'this:mt_publishPost',
128
129             // PingBack
130             'pingback.ping' => 'this:pingback_ping',
131             'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',
132
133             'demo.sayHello' => 'this:sayHello',
134             'demo.addTwoNumbers' => 'this:addTwoNumbers'
135         );
136         $this->methods = apply_filters('xmlrpc_methods', $this->methods);
137         $this->IXR_Server($this->methods);
138     }
139
140     function sayHello($args) {
141         return 'Hello!';
142     }
143
144     function addTwoNumbers($args) {
145         $number1 = $args[0];
146         $number2 = $args[1];
147         return $number1 + $number2;
148     }
149
150     function login_pass_ok($user_login, $user_pass) {
151         if (!user_pass_ok($user_login, $user_pass)) {
152             $this->error = new IXR_Error(403, __('Bad login/pass combination.'));
153             return false;
154         }
155         return true;
156     }
157
158     function escape(&$array) {
159         global $wpdb;
160
161         if(!is_array($array)) {
162             return($wpdb->escape($array));
163         }
164         else {
165             foreach ( (array) $array as $k => $v ) {
166                 if (is_array($v)) {
167                     $this->escape($array[$k]);
168                 } else if (is_object($v)) {
169                     //skip
170                 } else {
171                     $array[$k] = $wpdb->escape($v);
172                 }
173             }
174         }
175     }
176
177     function get_custom_fields($post_id) {
178         $post_id = (int) $post_id;
179
180         $custom_fields = array();
181
182         foreach ( (array) has_meta($post_id) as $meta ) {
183             // Don't expose protected fields.
184             if ( strpos($meta['meta_key'], '_wp_') === 0 ) {
185                 continue;
186             }
187
188             $custom_fields[] = array(
189                 "id"    => $meta['meta_id'],
190                 "key"   => $meta['meta_key'],
191                 "value" => $meta['meta_value']
192             );
193         }
194
195         return $custom_fields;
196     }
197
198     function set_custom_fields($post_id, $fields) {
199         $post_id = (int) $post_id;
200
201         foreach ( (array) $fields as $meta ) {
202             if ( isset($meta['id']) ) {
203                 $meta['id'] = (int) $meta['id'];
204
205                 if ( isset($meta['key']) ) {
206                     update_meta($meta['id'], $meta['key'], $meta['value']);
207                 }
208                 else {
209                     delete_meta($meta['id']);
210                 }
211             }
212             else {
213                 $_POST['metakeyinput'] = $meta['key'];
214                 $_POST['metavalue'] = $meta['value'];
215                 add_meta($post_id);
216             }
217         }
218     }
219
220     /**
221      * WordPress XML-RPC API
222      * wp_getPage
223      */
224     function wp_getPage($args) {
225         $this->escape($args);
226
227         $blog_id    = (int) $args[0];
228         $page_id    = (int) $args[1];
229         $username    = $args[2];
230         $password    = $args[3];
231
232         if(!$this->login_pass_ok($username, $password)) {
233             return($this->error);
234         }
235
236         set_current_user( 0, $username );
237         if( !current_user_can( 'edit_page', $page_id ) )
238             return new IXR_Error( 401, __( 'Sorry, you can not edit this page.' ) );
239
240         do_action('xmlrpc_call', 'wp.getPage');
241
242         // Lookup page info.
243         $page = get_page($page_id);
244
245         // If we found the page then format the data.
246         if($page->ID && ($page->post_type == "page")) {
247             // Get all of the page content and link.
248             $full_page = get_extended($page->post_content);
249             $link = post_permalink($page->ID);
250
251             // Get info the page parent if there is one.
252             $parent_title = "";
253             if(!empty($page->post_parent)) {
254                 $parent = get_page($page->post_parent);
255                 $parent_title = $parent->post_title;
256             }
257
258             // Determine comment and ping settings.
259             $allow_comments = ("open" == $page->comment_status) ? 1 : 0;
260             $allow_pings = ("open" == $page->ping_status) ? 1 : 0;
261
262             // Format page date.
263             $page_date = mysql2date("Ymd\TH:i:s", $page->post_date);
264             $page_date_gmt = mysql2date("Ymd\TH:i:s", $page->post_date_gmt);
265
266             // Pull the categories info together.
267             $categories = array();
268             foreach(wp_get_post_categories($page->ID) as $cat_id) {
269                 $categories[] = get_cat_name($cat_id);
270             }
271
272             // Get the author info.
273             $author = get_userdata($page->post_author);
274
275             $page_struct = array(
276                 "dateCreated"            => new IXR_Date($page_date),
277                 "userid"                => $page->post_author,
278                 "page_id"                => $page->ID,
279                 "page_status"            => $page->post_status,
280                 "description"            => $full_page["main"],
281                 "title"                    => $page->post_title,
282                 "link"                    => $link,
283                 "permaLink"                => $link,
284                 "categories"            => $categories,
285                 "excerpt"                => $page->post_excerpt,
286                 "text_more"                => $full_page["extended"],
287                 "mt_allow_comments"        => $allow_comments,
288                 "mt_allow_pings"        => $allow_pings,
289                 "wp_slug"                => $page->post_name,
290                 "wp_password"            => $page->post_password,
291                 "wp_author"                => $author->display_name,
292                 "wp_page_parent_id"        => $page->post_parent,
293                 "wp_page_parent_title"    => $parent_title,
294                 "wp_page_order"            => $page->menu_order,
295                 "wp_author_id"            => $author->ID,
296                 "wp_author_display_name"    => $author->display_name,
297                 "date_created_gmt"        => new IXR_Date($page_date_gmt),
298                 "custom_fields"            => $this->get_custom_fields($page_id)
299             );
300
301             return($page_struct);
302         }
303         // If the page doesn't exist indicate that.
304         else {
305             return(new IXR_Error(404, __("Sorry, no such page.")));
306         }
307     }
308
309     /**
310      * WordPress XML-RPC API
311       * wp_getPages
312      */
313     function wp_getPages($args) {
314         $this->escape($args);
315
316         $blog_id    = (int) $args[0];
317         $username    = $args[1];
318         $password    = $args[2];
319
320         if(!$this->login_pass_ok($username, $password)) {
321             return($this->error);
322         }
323
324         set_current_user( 0, $username );
325         if( !current_user_can( 'edit_pages' ) )
326             return new IXR_Error( 401, __( 'Sorry, you can not edit pages.' ) );
327
328         do_action('xmlrpc_call', 'wp.getPages');
329
330         // Lookup info on pages.
331         $pages = get_pages();
332         $num_pages = count($pages);
333
334         // If we have pages, put together their info.
335         if($num_pages >= 1) {
336             $pages_struct = array();
337
338             for($i = 0; $i < $num_pages; $i++) {
339                 $page = wp_xmlrpc_server::wp_getPage(array(
340                     $blog_id, $pages[$i]->ID, $username, $password
341                 ));
342                 $pages_struct[] = $page;
343             }
344
345             return($pages_struct);
346         }
347         // If no pages were found return an error.
348         else {
349             return(array());
350         }
351     }
352
353     /**
354      * WordPress XML-RPC API
355       * wp_newPage
356      */
357     function wp_newPage($args) {
358         // Items not escaped here will be escaped in newPost.
359         $username    = $this->escape($args[1]);
360         $password    = $this->escape($args[2]);
361         $page        = $args[3];
362         $publish    = $args[4];
363
364         if(!$this->login_pass_ok($username, $password)) {
365             return($this->error);
366         }
367
368         do_action('xmlrpc_call', 'wp.newPage');
369
370         // Set the user context and check if they are allowed
371         // to add new pages.
372         $user = set_current_user(0, $username);
373         if(!current_user_can("publish_pages")) {
374             return(new IXR_Error(401, __("Sorry, you can not add new pages.")));
375         }
376
377         // Mark this as content for a page.
378         $args[3]["post_type"] = "page";
379
380         // Let mw_newPost do all of the heavy lifting.
381         return($this->mw_newPost($args));
382     }
383
384     /**
385      * WordPress XML-RPC API
386      * wp_deletePage
387      */
388     function wp_deletePage($args) {
389         $this->escape($args);
390
391         $blog_id    = (int) $args[0];
392         $username    = $args[1];
393         $password    = $args[2];
394         $page_id    = (int) $args[3];
395
396         if(!$this->login_pass_ok($username, $password)) {
397             return($this->error);
398         }
399
400         do_action('xmlrpc_call', 'wp.deletePage');
401
402         // Get the current page based on the page_id and
403         // make sure it is a page and not a post.
404         $actual_page = wp_get_single_post($page_id, ARRAY_A);
405         if(
406             !$actual_page
407             || ($actual_page["post_type"] != "page")
408         ) {
409             return(new IXR_Error(404, __("Sorry, no such page.")));
410         }
411
412         // Set the user context and make sure they can delete pages.
413         set_current_user(0, $username);
414         if(!current_user_can("delete_page", $page_id)) {
415             return(new IXR_Error(401, __("Sorry, you do not have the right to delete this page.")));
416         }
417
418         // Attempt to delete the page.
419         $result = wp_delete_post($page_id);
420         if(!$result) {
421             return(new IXR_Error(500, __("Failed to delete the page.")));
422         }
423
424         return(true);
425     }
426
427     /**
428      * WordPress XML-RPC API
429      * wp_editPage
430      */
431     function wp_editPage($args) {
432         // Items not escaped here will be escaped in editPost.
433         $blog_id    = (int) $args[0];
434         $page_id    = (int) $this->escape($args[1]);
435         $username    = $this->escape($args[2]);
436         $password    = $this->escape($args[3]);
437         $content    = $args[4];
438         $publish    = $args[5];
439
440         if(!$this->login_pass_ok($username, $password)) {
441             return($this->error);
442         }
443
444         do_action('xmlrpc_call', 'wp.editPage');
445
446         // Get the page data and make sure it is a page.
447         $actual_page = wp_get_single_post($page_id, ARRAY_A);
448         if(
449             !$actual_page
450             || ($actual_page["post_type"] != "page")
451         ) {
452             return(new IXR_Error(404, __("Sorry, no such page.")));
453         }
454
455         // Set the user context and make sure they are allowed to edit pages.
456         set_current_user(0, $username);
457         if(!current_user_can("edit_page", $page_id)) {
458             return(new IXR_Error(401, __("Sorry, you do not have the right to edit this page.")));
459         }
460
461         // Mark this as content for a page.
462         $content["post_type"] = "page";
463
464         // Arrange args in the way mw_editPost understands.
465         $args = array(
466             $page_id,
467             $username,
468             $password,
469             $content,
470             $publish
471         );
472
473         // Let mw_editPost do all of the heavy lifting.
474         return($this->mw_editPost($args));
475     }
476
477     /**
478      * WordPress XML-RPC API
479      * wp_getPageList
480      */
481     function wp_getPageList($args) {
482         global $wpdb;
483
484         $this->escape($args);
485
486         $blog_id                = (int) $args[0];
487         $username                = $args[1];
488         $password                = $args[2];
489
490         if(!$this->login_pass_ok($username, $password)) {
491             return($this->error);
492         }
493
494         set_current_user( 0, $username );
495         if( !current_user_can( 'edit_pages' ) )
496             return new IXR_Error( 401, __( 'Sorry, you can not edit pages.' ) );
497
498         do_action('xmlrpc_call', 'wp.getPageList');
499
500         // Get list of pages ids and titles
501         $page_list = $wpdb->get_results("
502             SELECT ID page_id,
503                 post_title page_title,
504                 post_parent page_parent_id,
505                 post_date_gmt,
506  &n