PHP Cross Reference of WordPress Subversion HEAD |
| [ Index ] [ Classes ] [ Functions ] [ Variables ] [ Constants ] |
[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 // 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 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", $page->post_date); 212 $page_date_gmt = mysql2date("Ymd\TH:i:s", $page->post_date_gmt); 213 214 // Pull the categories info together. 215 $categories = array(); 216 foreach(wp_get_post_categories($page->ID) as $cat_id) { 217 $categories[] = get_cat_name($cat_id); 218 } 219 220 // Get the author info. 221 $author = get_userdata($page->post_author); 222 223 $page_struct = array( 224 "dateCreated" => new IXR_Date($page_date), 225 "userid" => $page->post_author, 226 "page_id" => $page->ID, 227 "page_status" => $page->post_status, 228 "description" => $full_page["main"], 229 "title" => $page->post_title, 230 "link" => $link, 231 "permaLink" => $link, 232 "categories" => $categories, 233 "excerpt" => $page->post_excerpt, 234 "text_more" => $full_page["extended"], 235 "mt_allow_comments" => $allow_comments, 236 "mt_allow_pings" => $allow_pings, 237 "wp_slug" => $page->post_name, 238 "wp_password" => $page->post_password, 239 "wp_author" => $author->display_name, 240 "wp_page_parent_id" => $page->post_parent, 241 "wp_page_parent_title" => $parent_title, 242 "wp_page_order" => $page->menu_order, 243 "wp_author_id" => $author->ID, 244 "wp_author_display_name" => $author->display_name, 245 "date_created_gmt" => new IXR_Date($page_date_gmt) 246 ); 247 248 return($page_struct); 249 } 250 // If the page doesn't exist indicate that. 251 else { 252 return(new IXR_Error(404, __("Sorry, no such page."))); 253 } 254 } 255 256 /** 257 * WordPress XML-RPC API 258 * wp_getPages 259 */ 260 function wp_getPages($args) { 261 $this->escape($args); 262 263 $blog_id = (int) $args[0]; 264 $username = $args[1]; 265 $password = $args[2]; 266 267 if(!$this->login_pass_ok($username, $password)) { 268 return($this->error); 269 } 270 271 // Lookup info on pages. 272 $pages = get_pages(); 273 $num_pages = count($pages); 274 275 // If we have pages, put together their info. 276 if($num_pages >= 1) { 277 $pages_struct = array(); 278 279 for($i = 0; $i < $num_pages; $i++) { 280 $page = wp_xmlrpc_server::wp_getPage(array( 281 $blog_id, $pages[$i]->ID, $username, $password 282 )); 283 $pages_struct[] = $page; 284 } 285 286 return($pages_struct); 287 } 288 // If no pages were found return an error. 289 else { 290 return(array()); 291 } 292 } 293 294 /** 295 * WordPress XML-RPC API 296 * wp_newPage 297 */ 298 function wp_newPage($args) { 299 // Items not escaped here will be escaped in newPost. 300 $username = $this->escape($args[1]); 301 $password = $this->escape($args[2]); 302 $page = $args[3]; 303 $publish = $args[4]; 304 305 if(!$this->login_pass_ok($username, $password)) { 306 return($this->error); 307 } 308 309 // Set the user context and check if they are allowed 310 // to add new pages. 311 $user = set_current_user(0, $username); 312 if(!current_user_can("publish_pages")) { 313 return(new IXR_Error(401, __("Sorry, you can not add new pages."))); 314 } 315 316 // Mark this as content for a page. 317 $args[3]["post_type"] = "page"; 318 319 // Let mw_newPost do all of the heavy lifting. 320 return($this->mw_newPost($args)); 321 } 322 323 /** 324 * WordPress XML-RPC API 325 * wp_deletePage 326 */ 327 function wp_deletePage($args) { 328 $this->escape($args); 329 330 $blog_id = (int) $args[0]; 331 $username = $args[1]; 332 $password = $args[2]; 333 $page_id = (int) $args[3]; 334 335 if(!$this->login_pass_ok($username, $password)) { 336 return($this->error); 337 } 338 339 // Get the current page based on the page_id and 340 // make sure it is a page and not a post. 341 $actual_page = wp_get_single_post($page_id, ARRAY_A); 342 if( 343 !$actual_page 344 || ($actual_page["post_type"] != "page") 345 ) { 346 return(new IXR_Error(404, __("Sorry, no such page."))); 347 } 348 349 // Set the user context and make sure they can delete pages. 350 set_current_user(0, $username); 351 if(!current_user_can("delete_page", $page_id)) { 352 return(new IXR_Error(401, __("Sorry, you do not have the right to delete this page."))); 353 } 354 355 // Attempt to delete the page. 356 $result = wp_delete_post($page_id); 357 if(!$result) { 358 return(new IXR_Error(500, __("Failed to delete the page."))); 359 } 360 361 return(true); 362 } 363 364 /** 365 * WordPress XML-RPC API 366 * wp_editPage 367 */ 368 function wp_editPage($args) { 369 // Items not escaped here will be escaped in editPost. 370 $blog_id = (int) $args[0]; 371 $page_id = (int) $this->escape($args[1]); 372 $username = $this->escape($args[2]); 373 $password = $this->escape($args[3]); 374 $content = $args[4]; 375 $publish = $args[5]; 376 377 if(!$this->login_pass_ok($username, $password)) { 378 return($this->error); 379 } 380 381 // Get the page data and make sure it is a page. 382 $actual_page = wp_get_single_post($page_id, ARRAY_A); 383 if( 384 !$actual_page 385 || ($actual_page["post_type"] != "page") 386 ) { 387 return(new IXR_Error(404, __("Sorry, no such page."))); 388 } 389 390 // Set the user context and make sure they are allowed to edit pages. 391 set_current_user(0, $username); 392 if(!current_user_can("edit_page", $page_id)) { 393 return(new IXR_Error(401, __("Sorry, you do not have the right to edit this page."))); 394 } 395 396 // Mark this as content for a page. 397 $content["post_type"] = "page"; 398 399 // Arrange args in the way mw_editPost understands. 400 $args = array( 401 $page_id, 402 $username, 403 $password, 404 $content, 405 $publish 406 ); 407 408 // Let mw_editPost do all of the heavy lifting. 409 return($this->mw_editPost($args)); 410 } 411 412 /** 413 * WordPress XML-RPC API 414 * wp_getPageList 415 */ 416 function wp_getPageList($args) { 417 global $wpdb; 418 419 $this->escape($args); 420 421 $blog_id = (int) $args[0]; 422 $username = $args[1]; 423 $password = $args[2]; 424 425 if(!$this->login_pass_ok($username, $password)) { 426 return($this->error); 427 } 428 429 // Get list of pages ids and titles 430 $page_list = $wpdb->get_results(" 431 SELECT ID page_id, 432 post_title page_title, 433 post_parent page_parent_id, 434 post_date_gmt, 435 post_date 436 FROM {$wpdb->posts} 437 WHERE post_type = 'page' 438 ORDER BY ID 439 "); 440 441 // The date needs to be formated properly. 442 $num_pages = count($page_list); 443 for($i = 0; $i < $num_pages; $i++) { 444 $post_date = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date); 445 $post_date_gmt = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date_gmt); 446 447 $page_list[$i]->dateCreated = new IXR_Date($post_date); 448 $page_list[$i]->date_created_gmt = new IXR_Date($post_date_gmt); 449 450 unset($page_list[$i]->post_date_gmt); 451 unset($page_list[$i]->post_date); 452 } 453 454 return($page_list); 455 } 456 457 /** 458 * WordPress XML-RPC API 459 * wp_getAuthors 460 */ 461 function wp_getAuthors($args) { 462 global $wpdb; 463 464 $this->escape($args); 465 466 $blog_id = (int) $args[0]; 467 $username = $args[1]; 468 $password = $args[2]; 469 470 if(!$this->login_pass_ok($username, $password)) { 471 return($this->error); 472 } 473 474 return(get_users_of_blog()); 475 } 476 477 /** 478 * WordPress XML-RPC API 479 * wp_newCategory 480 */ 481 function wp_newCategory($args) { 482 $this->escape($args); 483 484 $blog_id = (int) $args[0]; 485 $username = $args[1]; 486 $password = $args[2]; 487 $category = $args[3]; 488 489 if(!$this->login_pass_ok($username, $password)) { 490 return($this->error); 491 } 492 493 // Set the user context and make sure they are 494 // allowed to add a category. 495 set_current_user(0, $username); 496 if(!current_user_can("manage_categories", $page_id)) { 497 return(new IXR_Error(401, __("Sorry, you do not have the right to add a category."))); 498 } 499 500 // If no slug was provided make it empty so that 501 // WordPress will generate one. 502 if(empty($category["slug"])) { 503 $category["slug"] = ""; 504 } 505 506 // If no parent_id was provided make it empty 507 // so that it will be a top level page (no parent). 508 if ( !isset($category["parent_id"]) ) 509 $category["parent_id"] = ""; 510 511 // If no description was provided make it empty. 512 if(empty($category["description"])) { 513 $category["description"] = ""; 514 } 515 516 $new_category = array( 517 "cat_name" => $category["name"], 518 "category_nicename" => $category["slug"], 519 "category_parent" => $category["parent_id"], 520 "category_description" => $category["description"] 521 ); 522 523 $cat_id = wp_insert_category($new_category); 524 if(!$cat_id) { 525 return(new IXR_Error(500, __("Sorry, the new category failed."))); 526 } 527 528 return($cat_id); 529 } 530 531 /** 532 * WordPress XML-RPC API 533 * wp_suggestCategories 534 */ 535 function wp_suggestCategories($args) { 536 global $wpdb; 537 538 $this->escape($args); 539 540 $blog_id = (int) $args[0]; 541 $username = $args[1]; 542 $password = $args[2]; 543 $category = $args[3]; 544 $max_results = (int) $args[4]; 545 546 if(!$this->login_pass_ok($username, $password)) { 547 return($this->error); 548 } 549 550 $args = array('get' => 'all', 'number' => $max_results, 'name__like' => $category); 551 $category_suggestions = get_categories($args); 552 553 return($category_suggestions); 554 } 555 556 557 /* Blogger API functions 558 * specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/ 559 */ 560 561 562 /* blogger.getUsersBlogs will make more sense once we support multiple blogs */ 563 function blogger_getUsersBlogs($args) { 564 565 $this->escape($args); 566 567 $user_login = $args[1]; 568 $user_pass = $args[2]; 569 570 if (!$this->login_pass_ok(