| [ Index ] |
WordPress Source Cross Reference |
[Summary view] [Print] [Text view]
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 = $user->ID; 538 539 $post_title = $content_struct['title']; 540 $post_content = apply_filters( 'content_save_pre', $content_struct['description'] ); 541 $post_status = $publish ? 'publish' : 'draft'; 542 543 $post_excerpt = $content_struct['mt_excerpt']; 544 $post_more = $content_struct['mt_text_more']; 545 546 $comment_status = (empty($content_struct['mt_allow_comments'])) ? 547 get_settings('default_comment_status') 548 : $content_struct['mt_allow_comments']; 549 550 $ping_status = (empty($content_struct['mt_allow_pings'])) ? 551 get_settings('default_ping_status') 552 : $content_struct['mt_allow_pings']; 553 554 if ($post_more) { 555 $post_content = $post_content . "\n<!--more-->\n" . $post_more; 556 } 557 558 $to_ping = $content_struct['mt_tb_ping_urls']; 559 560 // Do some timestamp voodoo 561 $dateCreatedd = $content_struct['dateCreated']; 562 if (!empty($dateCreatedd)) { 563