root/tags/2.2.1/xmlrpc.php

Revision 5730, 55.6 kB (checked in by ryan, 1 year ago)

Accept 'open' and 'closed' as valid values for mt_allow_comments and mt_allow_pings in XML-RPC. Props Joseph Scott. For 2.2. see #4469

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
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 ?>
24 <?php echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>'; ?>
25 <rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">
26   <service>
27     <engineName>WordPress</engineName>
28     <engineLink>http://wordpress.org/</engineLink>
29     <homePageLink><?php bloginfo_rss('url') ?></homePageLink>
30     <apis>
31       <api name="WordPress" blogID="1" preferred="false" apiLink="<?php bloginfo_rss('wpurl') ?>/xmlrpc.php" />
32       <api name="Movable Type" blogID="1" preferred="true" apiLink="<?php bloginfo_rss('wpurl') ?>/xmlrpc.php" />
33       <api name="MetaWeblog" blogID="1" preferred="false" apiLink="<?php bloginfo_rss('wpurl') ?>/xmlrpc.php" />
34       <api name="Blogger" blogID="1" preferred="false" apiLink="<?php bloginfo_rss('wpurl') ?>/xmlrpc.php" />
35     </apis>
36   </service>
37 </rsd>
38 <?php
39 exit;
40 }
41
42 include_once(ABSPATH . 'wp-admin/admin-functions.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
73 class wp_xmlrpc_server extends IXR_Server {
74
75     function wp_xmlrpc_server() {
76         $this->methods = array(
77             // WordPress API
78             'wp.getPage'            => 'this:wp_getPage',
79             'wp.getPages'            => 'this:wp_getPages',
80             'wp.newPage'            => 'this:wp_newPage',
81             'wp.deletePage'            => 'this:wp_deletePage',
82             'wp.editPage'            => 'this:wp_editPage',
83             'wp.getPageList'        => 'this:wp_getPageList',
84             'wp.getAuthors'            => 'this:wp_getAuthors',
85             'wp.getCategories'        => 'this:mw_getCategories',        // Alias
86             'wp.newCategory'        => 'this:wp_newCategory',
87             'wp.suggestCategories'    => 'this:wp_suggestCategories',
88             'wp.uploadFile'            => 'this:mw_newMediaObject',    // Alias
89
90             // Blogger API
91             'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',
92             'blogger.getUserInfo' => 'this:blogger_getUserInfo',
93             'blogger.getPost' => 'this:blogger_getPost',
94             'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',
95             'blogger.getTemplate' => 'this:blogger_getTemplate',
96             'blogger.setTemplate' => 'this:blogger_setTemplate',
97             'blogger.newPost' => 'this:blogger_newPost',
98             'blogger.editPost' => 'this:blogger_editPost',
99             'blogger.deletePost' => 'this:blogger_deletePost',
100
101             // MetaWeblog API (with MT extensions to structs)
102             'metaWeblog.newPost' => 'this:mw_newPost',
103             'metaWeblog.editPost' => 'this:mw_editPost',
104             'metaWeblog.getPost' => 'this:mw_getPost',
105             'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts',
106             'metaWeblog.getCategories' => 'this:mw_getCategories',
107             'metaWeblog.newMediaObject' => 'this:mw_newMediaObject',
108
109             // MetaWeblog API aliases for Blogger API
110             // see http://www.xmlrpc.com/stories/storyReader$2460
111             'metaWeblog.deletePost' => 'this:blogger_deletePost',
112             'metaWeblog.getTemplate' => 'this:blogger_getTemplate',
113             'metaWeblog.setTemplate' => 'this:blogger_setTemplate',
114             'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',
115
116             // MovableType API
117             'mt.getCategoryList' => 'this:mt_getCategoryList',
118             'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles',
119             'mt.getPostCategories' => 'this:mt_getPostCategories',
120             'mt.setPostCategories' => 'this:mt_setPostCategories',
121             'mt.supportedMethods' => 'this:mt_supportedMethods',
122             'mt.supportedTextFilters' => 'this:mt_supportedTextFilters',
123             'mt.getTrackbackPings' => 'this:mt_getTrackbackPings',
124             'mt.publishPost' => 'this:mt_publishPost',
125
126             // PingBack
127             'pingback.ping' => 'this:pingback_ping',
128             'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',
129
130             'demo.sayHello' => 'this:sayHello',
131             'demo.addTwoNumbers' => 'this:addTwoNumbers'
132         );
133         $this->methods = apply_filters('xmlrpc_methods', $this->methods);
134         $this->IXR_Server($this->methods);
135     }
136
137     function sayHello($args) {
138         return 'Hello!';
139     }
140
141     function addTwoNumbers($args) {
142         $number1 = $args[0];
143         $number2 = $args[1];
144         return $number1 + $number2;
145     }
146
147     function login_pass_ok($user_login, $user_pass) {
148         if (!user_pass_ok($user_login, $user_pass)) {
149             $this->error = new IXR_Error(403, __('Bad login/pass combination.'));
150             return false;
151         }
152         return true;
153     }
154
155     function escape(&$array) {
156         global $wpdb;
157
158         if(!is_array($array)) {
159             return($wpdb->escape($array));
160         }
161         else {
162             foreach ( (array) $array as $k => $v ) {
163                 if (is_array($v)) {
164                     $this->escape($array[$k]);
165                 } else if (is_object($v)) {
166                     //skip
167                 } else {
168                     $array[$k] = $wpdb->escape($v);
169                 }
170             }
171         }
172     }
173
174     /**
175      * WordPress XML-RPC API
176      * wp_getPage
177      */
178     function wp_getPage($args) {
179         $this->escape($args);
180
181         $blog_id    = (int) $args[0];
182         $page_id    = (int) $args[1];
183         $username    = $args[2];
184         $password    = $args[3];
185
186         if(!$this->login_pass_ok($username, $password)) {
187             return($this->error);
188         }
189
190         // Lookup page info.
191         $page = get_page($page_id);
192
193         // If we found the page then format the data.
194         if($page->ID && ($page->post_type == "page")) {
195             // Get all of the page content and link.
196             $full_page = get_extended($page->post_content);
197             $link = post_permalink($page->ID);
198
199             // Get info the page parent if there is one.
200             $parent_title = "";
201             if(!empty($page->post_parent)) {
202                 $parent = get_page($page->post_parent);
203                 $parent_title = $parent->post_title;
204             }
205
206             // Determine comment and ping settings.
207             $allow_comments = ("open" == $page->comment_status) ? 1 : 0;
208             $allow_pings = ("open" == $page->ping_status) ? 1 : 0;
209
210             // Format page date.
211             $page_date = mysql2date("Ymd\TH:i:s\Z", $page->post_date_gmt);
212
213             // Pull the categories info together.
214             $categories = array();
215             foreach(wp_get_post_categories($page->ID) as $cat_id) {
216                 $categories[] = get_cat_name($cat_id);
217             }
218
219             // Get the author info.
220             $author = get_userdata($page->post_author);
221
222             $page_struct = array(
223                 "dateCreated"            => new IXR_Date($page_date),
224                 "userid"                => $page->post_author,
225                 "page_id"                => $page->ID,
226                 "page_status"            => $page->post_status,
227                 "description"            => $full_page["main"],
228                 "title"                    => $page->post_title,
229                 "link"                    => $link,
230                 "permaLink"                => $link,
231                 "categories"            => $categories,
232                 "excerpt"                => $page->post_excerpt,
233                 "text_more"                => $full_page["extended"],
234                 "mt_allow_comments"        => $allow_comments,
235                 "mt_allow_pings"        => $allow_pings,
236                 "wp_slug"                => $page->post_name,
237                 "wp_password"            => $page->post_password,
238                 "wp_author"                => $author->display_name,
239                 "wp_page_parent_id"        => $page->post_parent,
240                 "wp_page_parent_title"    => $parent_title,
241                 "wp_page_order"            => $page->menu_order,
242                 "wp_author_id"            => $author->ID,
243                 "wp_author_display_name"    => $author->display_name
244             );
245
246             return($page_struct);
247         }
248         // If the page doesn't exist indicate that.
249         else {
250             return(new IXR_Error(404, __("Sorry, no such page.")));
251         }
252     }
253
254     /**
255      * WordPress XML-RPC API
256       * wp_getPages
257      */
258     function wp_getPages($args) {
259         $this->escape($args);
260
261         $blog_id    = (int) $args[0];
262         $username    = $args[1];
263         $password    = $args[2];
264
265         if(!$this->login_pass_ok($username, $password)) {
266             return($this->error);
267         }
268
269         // Lookup info on pages.
270         $pages = get_pages();
271         $num_pages = count($pages);
272
273         // If we have pages, put together their info.
274         if($num_pages >= 1) {
275             $pages_struct = array();
276
277             for($i = 0; $i < $num_pages; $i++) {
278                 $page = wp_xmlrpc_server::wp_getPage(array(
279                     $blog_id, $pages[$i]->ID, $username, $password
280                 ));
281                 $pages_struct[] = $page;
282             }
283
284             return($pages_struct);
285         }
286         // If no pages were found return an error.
287         else {
288             return(array());
289         }
290     }
291
292     /**
293      * WordPress XML-RPC API
294       * wp_newPage
295      */
296     function wp_newPage($args) {
297         // Items not escaped here will be escaped in newPost.
298         $username    = $this->escape($args[1]);
299         $password    = $this->escape($args[2]);
300         $page        = $args[3];
301         $publish    = $args[4];
302
303         if(!$this->login_pass_ok($username, $password)) {
304             return($this->error);
305         }
306
307         // Set the user context and check if they are allowed
308         // to add new pages.
309         $user = set_current_user(0, $username);
310         if(!current_user_can("publish_pages")) {
311             return(new IXR_Error(401, __("Sorry, you can not add new pages.")));
312         }
313
314         // Mark this as content for a page.
315         $args[3]["post_type"] = "page";
316
317         // Let mw_newPost do all of the heavy lifting.
318         return($this->mw_newPost($args));
319     }
320
321     /**
322      * WordPress XML-RPC API
323      * wp_deletePage
324      */
325     function wp_deletePage($args) {
326         $this->escape($args);
327
328         $blog_id    = (int) $args[0];
329         $username    = $args[1];
330         $password    = $args[2];
331         $page_id    = (int) $args[3];
332
333         if(!$this->login_pass_ok($username, $password)) {
334             return($this->error);
335         }
336
337         // Get the current page based on the page_id and
338         // make sure it is a page and not a post.
339         $actual_page = wp_get_single_post($page_id, ARRAY_A);
340         if(
341             !$actual_page
342             || ($actual_page["post_type"] != "page")
343         ) {
344             return(new IXR_Error(404, __("Sorry, no such page.")));
345         }
346
347         // Set the user context and make sure they can delete pages.
348         set_current_user(0, $username);
349         if(!current_user_can("delete_page", $page_id)) {
350             return(new IXR_Error(401, __("Sorry, you do not have the right to delete this page.")));
351         }
352
353         // Attempt to delete the page.
354         $result = wp_delete_post($page_id);
355         if(!$result) {
356             return(new IXR_Error(500, __("Failed to delete the page.")));
357         }
358
359         return(true);
360     }
361
362     /**
363      * WordPress XML-RPC API
364      * wp_editPage
365      */
366     function wp_editPage($args) {
367         // Items not escaped here will be escaped in editPost.
368         $blog_id    = (int) $args[0];
369         $page_id    = (int) $this->escape($args[1]);
370         $username    = $this->escape($args[2]);
371         $password    = $this->escape($args[3]);
372         $content    = $args[4];
373         $publish    = $args[5];
374
375         if(!$this->login_pass_ok($username, $password)) {
376             return($this->error);
377         }
378
379         // Get the page data and make sure it is a page.
380         $actual_page = wp_get_single_post($page_id, ARRAY_A);
381         if(
382             !$actual_page
383             || ($actual_page["post_type"] != "page")
384         ) {
385             return(new IXR_Error(404, __("Sorry, no such page.")));
386         }
387
388         // Set the user context and make sure they are allowed to edit pages.
389         set_current_user(0, $username);
390         if(!current_user_can("edit_page", $page_id)) {
391             return(new IXR_Error(401, __("Sorry, you do not have the right to edit this page.")));
392         }
393
394         // Mark this as content for a page.
395         $content["post_type"] = "page";
396
397         // Arrange args in the way mw_editPost understands.
398         $args = array(
399             $page_id,
400             $username,
401             $password,
402             $content,
403             $publish
404         );
405
406         // Let mw_editPost do all of the heavy lifting.
407         return($this->mw_editPost($args));
408     }
409
410     /**
411      * WordPress XML-RPC API
412      * wp_getPageList
413      */
414     function wp_getPageList($args) {
415         global $wpdb;
416
417         $this->escape($args);
418
419         $blog_id                = (int) $args[0];
420         $username                = $args[1];
421         $password                = $args[2];
422
423         if(!$this->login_pass_ok($username, $password)) {
424             return($this->error);
425         }
426
427         // Get list of pages ids and titles
428         $page_list = $wpdb->get_results("
429             SELECT ID page_id,
430                 post_title page_title,
431                 post_parent page_parent_id,
432                 post_date_gmt
433             FROM {$wpdb->posts}
434             WHERE post_type = 'page'
435             ORDER BY ID
436         ");
437
438         // The date needs to be formated properly.
439         $num_pages = count($page_list);
440         for($i = 0; $i < $num_pages; $i++) {
441             $post_date = mysql2date("Ymd\TH:i:s\Z", $page_list[$i]->post_date_gmt);
442             $page_list[$i]->dateCreated = new IXR_Date($post_date);
443
444             unset($page_list[$i]->post_date_gmt);
445         }
446
447         return($page_list);
448     }
449
450     /**
451      * WordPress XML-RPC API
452      * wp_getAuthors
453      */
454     function wp_getAuthors($args) {
455         global $wpdb;
456
457         $this->escape($args);
458
459         $blog_id    = (int) $args[0];
460         $username    = $args[1];
461         $password    = $args[2];
462
463         if(!$this->login_pass_ok($username, $password)) {
464             return($this->error);
465         }
466
467         return(get_users_of_blog());
468     }
469
470     /**
471      * WordPress XML-RPC API
472      * wp_newCategory
473      */
474     function wp_newCategory($args) {
475         $this->escape($args);
476
477         $blog_id                = (int) $args[0];
478         $username                = $args[1];
479         $password                = $args[2];
480         $category                = $args[3];
481
482         if(!$this->login_pass_ok($username, $password)) {
483             return($this->error);
484         }
485
486         // Set the user context and make sure they are
487         // allowed to add a category.
488         set_current_user(0, $username);
489         if(!current_user_can("manage_categories", $page_id)) {
490             return(new IXR_Error(401, __("Sorry, you do not have the right to add a category.")));
491         }
492
493         // We need this to make use of the wp_insert_category()
494         // funciton.
495         require_once(ABSPATH . "wp-admin/admin-db.php");
496
497         // If no slug was provided make it empty so that
498         // WordPress will generate one.
499         if(empty($category["slug"])) {
500             $category["slug"] = "";
501         }
502
503         // If no parent_id was provided make it empty
504         // so that it will be a top level page (no parent).
505         if ( !isset($category["parent_id"]) )
506             $category["parent_id"] = "";
507
508         // If no description was provided make it empty.
509         if(empty($category["description"])) {
510             $category["description"] = "";
511         }
512     
513         $new_category = array(
514             "cat_name"                => $category["name"],
515             "category_nicename"        => $category["slug"],
516             "category_parent"        => $category["parent_id"],
517             "category_description"    => $category["description"