root/tags/1_0-rc1/xmlrpc.php

Revision 599, 36.3 kB (checked in by donncha, 2 years ago)

WP Merge

  • 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 # fix for mozBlog and other cases where '<?xml' isn't on the very first line
9 if ( isset($HTTP_RAW_POST_DATA) )
10     $HTTP_RAW_POST_DATA = trim($HTTP_RAW_POST_DATA);
11
12 include('./wp-config.php');
13
14 if ( isset( $_GET['rsd'] ) ) { // http://archipelago.phrasewise.com/rsd
15 header('Content-type: text/xml; charset=' . get_settings('blog_charset'), true);
16
17 ?>
18 <?php echo '<?xml version="1.0" encoding="'.get_settings('blog_charset').'"?'.'>'; ?>
19 <rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd">
20   <service>
21     <engineName>WordPress</engineName>
22     <engineLink>http://wordpress.org/</engineLink>
23     <homePageLink><?php bloginfo_rss('url') ?></homePageLink>
24     <apis>
25       <api name="Movable Type" blogID="1" preferred="true" apiLink="<?php bloginfo_rss('url') ?>/xmlrpc.php" />
26       <api name="MetaWeblog" blogID="1" preferred="false" apiLink="<?php bloginfo_rss('url') ?>/xmlrpc.php" />
27       <api name="Blogger" blogID="1" preferred="false" apiLink="<?php bloginfo_rss('url') ?>/xmlrpc.php" />
28     </apis>
29   </service>
30 </rsd>
31 <?php
32 exit;
33 }
34
35 include_once(ABSPATH . WPINC . '/class-IXR.php');
36
37 // Turn off all warnings and errors.
38 // error_reporting(0);
39
40 $post_default_title = ""; // posts submitted via the xmlrpc interface get that title
41
42 $xmlrpc_logging = 0;
43
44 function logIO($io,$msg) {
45     global $xmlrpc_logging;
46     if ($xmlrpc_logging) {
47         $fp = fopen("../xmlrpc.log","a+");
48         $date = gmdate("Y-m-d H:i:s ");
49         $iot = ($io == "I") ? " Input: " : " Output: ";
50         fwrite($fp, "\n\n".$date.$iot.$msg);
51         fclose($fp);
52     }
53     return true;
54     }
55
56 function starify($string) {
57     $i = strlen($string);
58     return str_repeat('*', $i);
59 }
60
61 logIO("I", $HTTP_RAW_POST_DATA);
62
63
64 function mkdir_p($target) {
65     // from php.net/mkdir user contributed notes
66     if (file_exists($target)) {
67       if (!is_dir($target)) {
68         return false;
69       } else {
70         return true;
71       }
72     }
73
74     // Attempting to create the directory may clutter up our display.
75     if (@mkdir($target)) {
76       return true;
77     }
78
79     // If the above failed, attempt to create the parent node, then try again.
80     if (mkdir_p(dirname($target))) {
81       return mkdir_p($target);
82     }
83
84     return false;
85 }
86
87
88 class wp_xmlrpc_server extends IXR_Server {
89
90     function wp_xmlrpc_server() {
91         $this->methods = array(
92           // Blogger API
93           'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',
94           'blogger.getUserInfo' => 'this:blogger_getUserInfo',
95           'blogger.getPost' => 'this:blogger_getPost',
96           'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',
97           'blogger.getTemplate' => 'this:blogger_getTemplate',
98           'blogger.setTemplate' => 'this:blogger_setTemplate',
99           'blogger.newPost' => 'this:blogger_newPost',
100           'blogger.editPost' => 'this:blogger_editPost',
101           'blogger.deletePost' => 'this:blogger_deletePost',
102
103           // MetaWeblog API (with MT extensions to structs)
104           'metaWeblog.newPost' => 'this:mw_newPost',
105           'metaWeblog.editPost' => 'this:mw_editPost',
106           'metaWeblog.getPost' => 'this:mw_getPost',
107           'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts',
108           'metaWeblog.getCategories' => 'this:mw_getCategories',
109           'metaWeblog.newMediaObject' => 'this:mw_newMediaObject',
110
111           // MetaWeblog API aliases for Blogger API
112           // see http://www.xmlrpc.com/stories/storyReader$2460
113           'metaWeblog.deletePost' => 'this:blogger_deletePost',
114           'metaWeblog.getTemplate' => 'this:blogger_getTemplate',
115           'metaWeblog.setTemplate' => 'this:blogger_setTemplate',
116           'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',
117
118           // MovableType API
119           'mt.getCategoryList' => 'this:mt_getCategoryList',
120           'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles',
121           'mt.getPostCategories' => 'this:mt_getPostCategories',
122           'mt.setPostCategories' => 'this:mt_setPostCategories',
123           'mt.supportedMethods' => 'this:mt_supportedMethods',
124           'mt.supportedTextFilters' => 'this:mt_supportedTextFilters',
125           'mt.getTrackbackPings' => 'this:mt_getTrackbackPings',
126           'mt.publishPost' => 'this:mt_publishPost',
127
128           // PingBack
129           'pingback.ping' => 'this:pingback_ping',
130           'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',
131
132           'demo.sayHello' => 'this:sayHello',
133           'demo.addTwoNumbers' => 'this:addTwoNumbers'
134         );
135         $this->methods = apply_filters('xmlrpc_methods', $this->methods);
136         $this->IXR_Server($this->methods);
137     }
138
139     function sayHello($args) {
140         return 'Hello!';
141     }
142
143     function addTwoNumbers($args) {
144         $number1 = $args[0];
145         $number2 = $args[1];
146         return $number1 + $number2;
147     }
148
149     function login_pass_ok($user_login, $user_pass) {
150       if (!user_pass_ok($user_login, $user_pass)) {
151         $this->error = new IXR_Error(403, 'Bad login/pass combination.');
152         return false;
153       }
154       return true;
155     }
156
157     function escape(&$array) {
158         global $wpdb;
159
160         foreach ($array as $k => $v) {
161             if (is_array($v)) {
162                 $this->escape($array[$k]);
163             } else if (is_object($v)) {
164                 //skip
165             } else {
166                 $array[$k] = $wpdb->escape($v);
167             }
168         }
169     }
170
171     /* Blogger API functions
172      * specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/
173      */
174
175
176     /* blogger.getUsersBlogs will make more sense once we support multiple blogs */
177     function blogger_getUsersBlogs($args) {
178
179         $this->escape($args);
180
181       $user_login = $args[1];
182       $user_pass  = $args[2];
183
184       if (!$this->login_pass_ok($user_login, $user_pass)) {
185         return $this->error;
186       }
187
188       set_current_user(0, $user_login);
189       $is_admin = current_user_can('level_8');
190
191       $struct = array(
192         'isAdmin'  => $is_admin,
193         'url'      => get_settings('home') . '/',
194         'blogid'   => '1',
195         'blogName' => get_settings('blogname')
196       );
197
198       return array($struct);
199     }
200
201
202     /* blogger.getUsersInfo gives your client some info about you, so you don't have to */
203     function blogger_getUserInfo($args) {
204
205         $this->escape($args);
206
207       $user_login = $args[1];
208       $user_pass  = $args[2];
209
210       if (!$this->login_pass_ok($user_login, $user_pass)) {
211         return $this->error;
212       }
213
214       $user_data = get_userdatabylogin($user_login);
215
216       $struct = array(
217         'nickname'  => $user_data->nickname,
218         'userid'    => $user_data->ID,
219         'url'       => $user_data->user_url,
220         'email'     => $user_data->user_email,
221         'lastname'  => $user_data->last_name,
222         'firstname' => $user_data->first_name
223       );
224
225       return $struct;
226     }
227
228
229     /* blogger.getPost ...gets a post */
230     function blogger_getPost($args) {
231
232         $this->escape($args);
233
234       $post_ID    = $args[1];
235       $user_login = $args[2];
236       $user_pass  = $args[3];
237
238       if (!$this->login_pass_ok($user_login, $user_pass)) {
239         return $this->error;
240       }
241
242       $user_data = get_userdatabylogin($user_login);
243       $post_data = wp_get_single_post($post_ID, ARRAY_A);
244
245       $categories = implode(',', wp_get_post_categories($post_ID));
246
247       $content  = '<title>'.stripslashes($post_data['post_title']).'</title>';
248       $content .= '<category>'.$categories.'</category>';
249       $content .= stripslashes($post_data['post_content']);
250
251       $struct = array(
252         'userid'    => $post_data['post_author'],
253         'dateCreated' => new IXR_Date(mysql2date('Ymd\TH:i:s', $post_data['post_date'])),
254         'content'     => $content,
255         'postid'  => $post_data['ID']
256       );
257
258       return $struct;
259     }
260
261
262     /* blogger.getRecentPosts ...gets recent posts */
263     function blogger_getRecentPosts($args) {
264
265       global $wpdb;
266
267         $this->escape($args);
268
269       $blog_ID    = $args[1]; /* though we don't use it yet */
270       $user_login = $args[2];
271       $user_pass  = $args[3];
272       $num_posts  = $args[4];
273
274       if (!$this->login_pass_ok($user_login, $user_pass)) {
275         return $this->error;
276       }
277
278       $posts_list = wp_get_recent_posts($num_posts);
279
280       if (!$posts_list) {
281         $this->error = new IXR_Error(500, 'Either there are no posts, or something went wrong.');
282         return $this->error;
283       }
284
285       foreach ($posts_list as $entry) {
286       
287         $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date']);
288         $categories = implode(',', wp_get_post_categories($entry['ID']));
289
290         $content  = '<title>'.stripslashes($entry['post_title']).'</title>';
291         $content .= '<category>'.$categories.'</category>';
292         $content .= stripslashes($entry['post_content']);
293
294         $struct[] = array(
295           'userid' => $entry['post_author'],
296           'dateCreated' => new IXR_Date($post_date),
297           'content' => $content,
298           'postid' => $entry['ID'],
299         );
300
301       }
302
303       $recent_posts = array();
304       for ($j=0; $j<count($struct); $j++) {
305         array_push($recent_posts, $struct[$j]);
306       }
307
308       return $recent_posts;
309     }
310
311
312     /* blogger.getTemplate returns your blog_filename */
313     function blogger_getTemplate($args) {
314
315         $this->escape($args);
316
317       $blog_ID    = $args[1];
318       $user_login = $args[2];
319       $user_pass  = $args[3];
320       $template   = $args[4]; /* could be 'main' or 'archiveIndex', but we don't use it */
321
322       if (!$this->login_pass_ok($user_login, $user_pass)) {
323         return $this->error;
324       }
325
326       set_current_user(0, $user_login);
327       if ( !current_user_can('edit_themes') ) {
328         return new IXR_Error(401, 'Sorry, this user can not edit the template.');
329       }
330
331       /* warning: here we make the assumption that the weblog's URI is on the same server */
332       $filename = get_settings('home') . '/';
333       $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);
334
335       $f = fopen($filename, 'r');
336       $content = fread($f, filesize($filename));
337       fclose($f);
338
339       /* so it is actually editable with a windows/mac client */
340       // FIXME: (or delete me) do we really want to cater to bad clients at the expense of good ones by BEEPing up their line breaks? commented.     $content = str_replace("\n", "\r\n", $content);
341
342       return $content;
343     }
344
345
346     /* blogger.setTemplate updates the content of blog_filename */
347     function blogger_setTemplate($args) {
348
349         $this->escape($args);
350
351       $blog_ID    = $args[1];
352       $user_login = $args[2];
353       $user_pass  = $args[3];
354       $content    = $args[4];
355       $template   = $args[5]; /* could be 'main' or 'archiveIndex', but we don't use it */
356
357       if (!$this->login_pass_ok($user_login, $user_pass)) {
358         return $this->error;
359       }
360
361       set_current_user(0, $user_login);
362       if ( !current_user_can('edit_themes') ) {
363         return new IXR_Error(401, 'Sorry, this user can not edit the template.');
364       }
365
366       /* warning: here we make the assumption that the weblog's URI is on the same server */
367       $filename = get_settings('home') . '/';
368       $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);
369
370       if ($f = fopen($filename, 'w+')) {
371         fwrite($f, $content);
372         fclose($f);
373       } else {
374         return new IXR_Error(500, 'Either the file is not writable, or something wrong happened. The file has not been updated.');
375       }
376
377       return true;
378     }
379
380
381     /* blogger.newPost ...creates a new post */
382     function blogger_newPost($args) {
383
384       global $wpdb;
385
386         $this->escape($args);
387
388       $blog_ID    = $args[1]; /* though we don't use it yet */
389       $user_login = $args[2];
390       $user_pass  = $args[3];
391       $content    = $args[4];
392       $publish    = $args[5];
393
394       if (!$this->login_pass_ok($user_login, $user_pass)) {
395         return $this->error;
396       }
397       
398       $cap = ($publish) ? 'publish_posts' : 'edit_posts';
399       $user = set_current_user(0, $user_login);
400       if ( !current_user_can($cap) )
401         return new IXR_Error(401, 'Sorry, you can not post on this weblog or category.');
402
403       $post_status = ($publish) ? 'publish' : 'draft';
404
405       $post_author = $user->ID;
406
407       $post_title = xmlrpc_getposttitle($content);
408       $post_category = xmlrpc_getpostcategory($content);
409       $post_content = xmlrpc_removepostdata($content);
410
411       $post_date = current_time('mysql');
412       $post_date_gmt = current_time('mysql', 1);
413
414       $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status');
415
416       $post_ID = wp_insert_post($post_data);
417
418       if (!$post_ID) {
419         return new IXR_Error(500, 'Sorry, your entry could not be posted. Something wrong happened.');
420       }
421
422       logIO('O', "Posted ! ID: $post_ID");
423
424       return $post_ID;
425     }
426
427
428     /* blogger.editPost ...edits a post */
429     function blogger_editPost($args) {
430
431       global $wpdb;
432
433         $this->escape($args);
434
435       $post_ID     = $args[1];
436       $user_login  = $args[2];
437       $user_pass   = $args[3];
438       $content     = $args[4];
439       $publish     = $args[5];
440
441       if (!$this->login_pass_ok($user_login, $user_pass)) {
442         return $this->error;
443       }
444
445       $actual_post = wp_get_single_post($post_ID,ARRAY_A);
446
447       if (!$actual_post) {
448           return new IXR_Error(404, 'Sorry, no such post.');
449       }
450
451         $this->escape($actual_post);
452
453       set_current_user(0, $user_login);
454       if ( !current_user_can('edit_post', $post_ID) )
455         return new IXR_Error(401, 'Sorry, you do not have the right to edit this post.');
456
457       extract($actual_post);
458
459       $post_title = xmlrpc_getposttitle($content);
460       $post_category = xmlrpc_getpostcategory($content);
461       $post_content = xmlrpc_removepostdata($content);
462
463       $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt');
464
465       $result = wp_update_post($postdata);
466
467       if (!$result) {
468           return new IXR_Error(500, 'For some strange yet very annoying reason, this post could not be edited.');
469       }
470
471       return true;
472     }
473
474
475     /* blogger.deletePost ...deletes a post */
476     function blogger_deletePost($args) {
477
478       global $wpdb;
479
480         $this->escape($args);
481
482       $post_ID     = $args[1];
483       $user_login  = $args[2];
484       $user_pass   = $args[3];
485       $publish     = $args[4];
486
487       if (!$this->login_pass_ok($user_login, $user_pass)) {
488         return $this->error;
489       }
490
491       $actual_post = wp_get_single_post($post_ID,ARRAY_A);
492
493       if (!$actual_post) {
494           return new IXR_Error(404, 'Sorry, no such post.');
495       }
496
497       set_current_user(0, $user_login);
498       if ( !current_user_can('edit_post', $post_ID) )
499         return new IXR_Error(401, 'Sorry, you do not have the right to delete this post.');
500
501       $result = wp_delete_post($post_ID);
502
503       if (!$result) {
504           return new IXR_Error(500, 'For some strange yet very annoying reason, this post could not be deleted.');
505       }
506
507       return true;
508     }
509
510
511
512     /* MetaWeblog API functions
513      * specs on wherever Dave Winer wants them to be
514      */
515
516     /* metaweblog.newPost creates a post */
517     function mw_newPost($args) {
518
519       global $wpdb, $post_default_category;
520
521         $this->escape($args);
522
523       $blog_ID     = $args[0]; // we will support this in the near future
524       $user_login  = $args[1];
525       $user_pass   = $args[2];
526       $content_struct = $args[3];
527       $publish     = $args[4];
528
529       if (!$this->login_pass_ok($user_login, $user_pass)) {
530         return $this->error;
531       }
532
533       $user = set_current_user(0, $user_login);
534       if ( !current_user_can('publish_posts') )
535         return new IXR_Error(401, 'Sorry, you can not post on this weblog or category.');
536
537       $post_author =