Make WordPress Core

Ticket #14820: move-wp-xmlrpc-server-class.14820.diff

File move-wp-xmlrpc-server-class.14820.diff, 186.4 KB (added by filosofo, 14 years ago)
  • wp-includes/class-xmlrpc-server.php

     
     1<?php
     2
     3/**
     4 * logIO() - Writes logging info to a file.
     5 *
     6 * @uses $xmlrpc_logging
     7 * @package WordPress
     8 * @subpackage Logging
     9 *
     10 * @param string $io Whether input or output
     11 * @param string $msg Information describing logging reason.
     12 * @return bool Always return true
     13 */
     14function logIO($io,$msg) {
     15        global $xmlrpc_logging;
     16        if ($xmlrpc_logging) {
     17                $fp = fopen("../xmlrpc.log","a+");
     18                $date = gmdate("Y-m-d H:i:s ");
     19                $iot = ($io == "I") ? " Input: " : " Output: ";
     20                fwrite($fp, "\n\n".$date.$iot.$msg);
     21                fclose($fp);
     22        }
     23        return true;
     24}
     25
     26/**
     27 * WordPress XMLRPC server implementation.
     28 *
     29 * Implements compatability for Blogger API, MetaWeblog API, MovableType, and
     30 * pingback. Additional WordPress API for managing comments, pages, posts,
     31 * options, etc.
     32 *
     33 * Since WordPress 2.6.0, WordPress XMLRPC server can be disabled in the
     34 * administration panels.
     35 *
     36 * @package WordPress
     37 * @subpackage Publishing
     38 * @since 1.5.0
     39 */
     40class wp_xmlrpc_server extends IXR_Server {
     41
     42        /**
     43         * Register all of the XMLRPC methods that XMLRPC server understands.
     44         *
     45         * PHP4 constructor and sets up server and method property. Passes XMLRPC
     46         * methods through the 'xmlrpc_methods' filter to allow plugins to extend
     47         * or replace XMLRPC methods.
     48         *
     49         * @since 1.5.0
     50         *
     51         * @return wp_xmlrpc_server
     52         */
     53        function wp_xmlrpc_server() {
     54                $this->methods = array(
     55                        // WordPress API
     56                        'wp.getUsersBlogs'              => 'this:wp_getUsersBlogs',
     57                        'wp.getPage'                    => 'this:wp_getPage',
     58                        'wp.getPages'                   => 'this:wp_getPages',
     59                        'wp.newPage'                    => 'this:wp_newPage',
     60                        'wp.deletePage'                 => 'this:wp_deletePage',
     61                        'wp.editPage'                   => 'this:wp_editPage',
     62                        'wp.getPageList'                => 'this:wp_getPageList',
     63                        'wp.getAuthors'                 => 'this:wp_getAuthors',
     64                        'wp.getCategories'              => 'this:mw_getCategories',             // Alias
     65                        'wp.getTags'                    => 'this:wp_getTags',
     66                        'wp.newCategory'                => 'this:wp_newCategory',
     67                        'wp.deleteCategory'             => 'this:wp_deleteCategory',
     68                        'wp.suggestCategories'  => 'this:wp_suggestCategories',
     69                        'wp.uploadFile'                 => 'this:mw_newMediaObject',    // Alias
     70                        'wp.getCommentCount'    => 'this:wp_getCommentCount',
     71                        'wp.getPostStatusList'  => 'this:wp_getPostStatusList',
     72                        'wp.getPageStatusList'  => 'this:wp_getPageStatusList',
     73                        'wp.getPageTemplates'   => 'this:wp_getPageTemplates',
     74                        'wp.getOptions'                 => 'this:wp_getOptions',
     75                        'wp.setOptions'                 => 'this:wp_setOptions',
     76                        'wp.getComment'                 => 'this:wp_getComment',
     77                        'wp.getComments'                => 'this:wp_getComments',
     78                        'wp.deleteComment'              => 'this:wp_deleteComment',
     79                        'wp.editComment'                => 'this:wp_editComment',
     80                        'wp.newComment'                 => 'this:wp_newComment',
     81                        'wp.getCommentStatusList' => 'this:wp_getCommentStatusList',
     82
     83                        // Blogger API
     84                        'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',
     85                        'blogger.getUserInfo' => 'this:blogger_getUserInfo',
     86                        'blogger.getPost' => 'this:blogger_getPost',
     87                        'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',
     88                        'blogger.getTemplate' => 'this:blogger_getTemplate',
     89                        'blogger.setTemplate' => 'this:blogger_setTemplate',
     90                        'blogger.newPost' => 'this:blogger_newPost',
     91                        'blogger.editPost' => 'this:blogger_editPost',
     92                        'blogger.deletePost' => 'this:blogger_deletePost',
     93
     94                        // MetaWeblog API (with MT extensions to structs)
     95                        'metaWeblog.newPost' => 'this:mw_newPost',
     96                        'metaWeblog.editPost' => 'this:mw_editPost',
     97                        'metaWeblog.getPost' => 'this:mw_getPost',
     98                        'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts',
     99                        'metaWeblog.getCategories' => 'this:mw_getCategories',
     100                        'metaWeblog.newMediaObject' => 'this:mw_newMediaObject',
     101
     102                        // MetaWeblog API aliases for Blogger API
     103                        // see http://www.xmlrpc.com/stories/storyReader$2460
     104                        'metaWeblog.deletePost' => 'this:blogger_deletePost',
     105                        'metaWeblog.getTemplate' => 'this:blogger_getTemplate',
     106                        'metaWeblog.setTemplate' => 'this:blogger_setTemplate',
     107                        'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',
     108
     109                        // MovableType API
     110                        'mt.getCategoryList' => 'this:mt_getCategoryList',
     111                        'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles',
     112                        'mt.getPostCategories' => 'this:mt_getPostCategories',
     113                        'mt.setPostCategories' => 'this:mt_setPostCategories',
     114                        'mt.supportedMethods' => 'this:mt_supportedMethods',
     115                        'mt.supportedTextFilters' => 'this:mt_supportedTextFilters',
     116                        'mt.getTrackbackPings' => 'this:mt_getTrackbackPings',
     117                        'mt.publishPost' => 'this:mt_publishPost',
     118
     119                        // PingBack
     120                        'pingback.ping' => 'this:pingback_ping',
     121                        'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',
     122
     123                        'demo.sayHello' => 'this:sayHello',
     124                        'demo.addTwoNumbers' => 'this:addTwoNumbers'
     125                );
     126
     127                $this->initialise_blog_option_info( );
     128                $this->methods = apply_filters('xmlrpc_methods', $this->methods);
     129        }
     130
     131        function serve_request() {
     132                $this->IXR_Server($this->methods);
     133        }
     134
     135        /**
     136         * Test XMLRPC API by saying, "Hello!" to client.
     137         *
     138         * @since 1.5.0
     139         *
     140         * @param array $args Method Parameters.
     141         * @return string
     142         */
     143        function sayHello($args) {
     144                return 'Hello!';
     145        }
     146
     147        /**
     148         * Test XMLRPC API by adding two numbers for client.
     149         *
     150         * @since 1.5.0
     151         *
     152         * @param array $args Method Parameters.
     153         * @return int
     154         */
     155        function addTwoNumbers($args) {
     156                $number1 = $args[0];
     157                $number2 = $args[1];
     158                return $number1 + $number2;
     159        }
     160
     161        /**
     162         * Check user's credentials.
     163         *
     164         * @since 1.5.0
     165         *
     166         * @param string $user_login User's username.
     167         * @param string $user_pass User's password.
     168         * @return bool Whether authentication passed.
     169         * @deprecated use wp_xmlrpc_server::login
     170         * @see wp_xmlrpc_server::login
     171         */
     172        function login_pass_ok($user_login, $user_pass) {
     173                if ( !get_option( 'enable_xmlrpc' ) ) {
     174                        $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site.  An admin user can enable them at %s'),  admin_url('options-writing.php') ) );
     175                        return false;
     176                }
     177
     178                if (!user_pass_ok($user_login, $user_pass)) {
     179                        $this->error = new IXR_Error(403, __('Bad login/pass combination.'));
     180                        return false;
     181                }
     182                return true;
     183        }
     184
     185        /**
     186         * Log user in.
     187         *
     188         * @since 2.8
     189         *
     190         * @param string $username User's username.
     191         * @param string $password User's password.
     192         * @return mixed WP_User object if authentication passed, false otherwise
     193         */
     194        function login($username, $password) {
     195                if ( !get_option( 'enable_xmlrpc' ) ) {
     196                        $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site.  An admin user can enable them at %s'),  admin_url('options-writing.php') ) );
     197                        return false;
     198                }
     199
     200                $user = wp_authenticate($username, $password);
     201
     202                if (is_wp_error($user)) {
     203                        $this->error = new IXR_Error(403, __('Bad login/pass combination.'));
     204                        return false;
     205                }
     206
     207                wp_set_current_user( $user->ID );
     208                return $user;
     209        }
     210
     211        /**
     212         * Sanitize string or array of strings for database.
     213         *
     214         * @since 1.5.2
     215         *
     216         * @param string|array $array Sanitize single string or array of strings.
     217         * @return string|array Type matches $array and sanitized for the database.
     218         */
     219        function escape(&$array) {
     220                global $wpdb;
     221
     222                if (!is_array($array)) {
     223                        return($wpdb->escape($array));
     224                } else {
     225                        foreach ( (array) $array as $k => $v ) {
     226                                if ( is_array($v) ) {
     227                                        $this->escape($array[$k]);
     228                                } else if ( is_object($v) ) {
     229                                        //skip
     230                                } else {
     231                                        $array[$k] = $wpdb->escape($v);
     232                                }
     233                        }
     234                }
     235        }
     236
     237        /**
     238         * Retrieve custom fields for post.
     239         *
     240         * @since 2.5.0
     241         *
     242         * @param int $post_id Post ID.
     243         * @return array Custom fields, if exist.
     244         */
     245        function get_custom_fields($post_id) {
     246                $post_id = (int) $post_id;
     247
     248                $custom_fields = array();
     249
     250                foreach ( (array) has_meta($post_id) as $meta ) {
     251                        // Don't expose protected fields.
     252                        if ( strpos($meta['meta_key'], '_wp_') === 0 ) {
     253                                continue;
     254                        }
     255
     256                        $custom_fields[] = array(
     257                                "id"    => $meta['meta_id'],
     258                                "key"   => $meta['meta_key'],
     259                                "value" => $meta['meta_value']
     260                        );
     261                }
     262
     263                return $custom_fields;
     264        }
     265
     266        /**
     267         * Set custom fields for post.
     268         *
     269         * @since 2.5.0
     270         *
     271         * @param int $post_id Post ID.
     272         * @param array $fields Custom fields.
     273         */
     274        function set_custom_fields($post_id, $fields) {
     275                $post_id = (int) $post_id;
     276
     277                foreach ( (array) $fields as $meta ) {
     278                        if ( isset($meta['id']) ) {
     279                                $meta['id'] = (int) $meta['id'];
     280
     281                                if ( isset($meta['key']) ) {
     282                                        update_meta($meta['id'], $meta['key'], $meta['value']);
     283                                }
     284                                else {
     285                                        delete_meta($meta['id']);
     286                                }
     287                        }
     288                        else {
     289                                $_POST['metakeyinput'] = $meta['key'];
     290                                $_POST['metavalue'] = $meta['value'];
     291                                add_meta($post_id);
     292                        }
     293                }
     294        }
     295
     296        /**
     297         * Set up blog options property.
     298         *
     299         * Passes property through 'xmlrpc_blog_options' filter.
     300         *
     301         * @since 2.6.0
     302         */
     303        function initialise_blog_option_info( ) {
     304                global $wp_version;
     305
     306                $this->blog_options = array(
     307                        // Read only options
     308                        'software_name'         => array(
     309                                'desc'                  => __( 'Software Name' ),
     310                                'readonly'              => true,
     311                                'value'                 => 'WordPress'
     312                        ),
     313                        'software_version'      => array(
     314                                'desc'                  => __( 'Software Version' ),
     315                                'readonly'              => true,
     316                                'value'                 => $wp_version
     317                        ),
     318                        'blog_url'                      => array(
     319                                'desc'                  => __( 'Site URL' ),
     320                                'readonly'              => true,
     321                                'option'                => 'siteurl'
     322                        ),
     323
     324                        // Updatable options
     325                        'time_zone'                     => array(
     326                                'desc'                  => __( 'Time Zone' ),
     327                                'readonly'              => false,
     328                                'option'                => 'gmt_offset'
     329                        ),
     330                        'blog_title'            => array(
     331                                'desc'                  => __( 'Site Title' ),
     332                                'readonly'              => false,
     333                                'option'                        => 'blogname'
     334                        ),
     335                        'blog_tagline'          => array(
     336                                'desc'                  => __( 'Site Tagline' ),
     337                                'readonly'              => false,
     338                                'option'                => 'blogdescription'
     339                        ),
     340                        'date_format'           => array(
     341                                'desc'                  => __( 'Date Format' ),
     342                                'readonly'              => false,
     343                                'option'                => 'date_format'
     344                        ),
     345                        'time_format'           => array(
     346                                'desc'                  => __( 'Time Format' ),
     347                                'readonly'              => false,
     348                                'option'                => 'time_format'
     349                        ),
     350                        'users_can_register'    => array(
     351                                'desc'                  => __( 'Allow new users to sign up' ),
     352                                'readonly'              => false,
     353                                'option'                => 'users_can_register'
     354                        )
     355                );
     356
     357                $this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options );
     358        }
     359
     360        /**
     361         * Retrieve the blogs of the user.
     362         *
     363         * @since 2.6.0
     364         *
     365         * @param array $args Method parameters.
     366         * @return array
     367         */
     368        function wp_getUsersBlogs( $args ) {
     369                global $current_site;
     370                // If this isn't on WPMU then just use blogger_getUsersBlogs
     371                if ( !is_multisite() ) {
     372                        array_unshift( $args, 1 );
     373                        return $this->blogger_getUsersBlogs( $args );
     374                }
     375
     376                $this->escape( $args );
     377
     378                $username = $args[0];
     379                $password = $args[1];
     380
     381                if ( !$user = $this->login($username, $password) )
     382                        return $this->error;
     383
     384
     385                do_action( 'xmlrpc_call', 'wp.getUsersBlogs' );
     386
     387                $blogs = (array) get_blogs_of_user( $user->ID );
     388                $struct = array( );
     389
     390                foreach ( $blogs as $blog ) {
     391                        // Don't include blogs that aren't hosted at this site
     392                        if ( $blog->site_id != $current_site->id )
     393                                continue;
     394
     395                        $blog_id = $blog->userblog_id;
     396                        switch_to_blog($blog_id);
     397                        $is_admin = current_user_can('manage_options');
     398
     399                        $struct[] = array(
     400                                'isAdmin'               => $is_admin,
     401                                'url'                   => get_option( 'home' ) . '/',
     402                                'blogid'                => $blog_id,
     403                                'blogName'              => get_option( 'blogname' ),
     404                                'xmlrpc'                => site_url( 'xmlrpc.php' )
     405                        );
     406
     407                        restore_current_blog( );
     408                }
     409
     410                return $struct;
     411        }
     412
     413        /**
     414         * Retrieve page.
     415         *
     416         * @since 2.2.0
     417         *
     418         * @param array $args Method parameters.
     419         * @return array
     420         */
     421        function wp_getPage($args) {
     422                $this->escape($args);
     423
     424                $blog_id        = (int) $args[0];
     425                $page_id        = (int) $args[1];
     426                $username       = $args[2];
     427                $password       = $args[3];
     428
     429                if ( !$user = $this->login($username, $password) ) {
     430                        return $this->error;
     431                }
     432
     433                if ( !current_user_can( 'edit_page', $page_id ) )
     434                        return new IXR_Error( 401, __( 'Sorry, you cannot edit this page.' ) );
     435
     436                do_action('xmlrpc_call', 'wp.getPage');
     437
     438                // Lookup page info.
     439                $page = get_page($page_id);
     440
     441                // If we found the page then format the data.
     442                if ( $page->ID && ($page->post_type == "page") ) {
     443                        // Get all of the page content and link.
     444                        $full_page = get_extended($page->post_content);
     445                        $link = post_permalink($page->ID);
     446
     447                        // Get info the page parent if there is one.
     448                        $parent_title = "";
     449                        if ( !empty($page->post_parent) ) {
     450                                $parent = get_page($page->post_parent);
     451                                $parent_title = $parent->post_title;
     452                        }
     453
     454                        // Determine comment and ping settings.
     455                        $allow_comments = comments_open($page->ID) ? 1 : 0;
     456                        $allow_pings = pings_open($page->ID) ? 1 : 0;
     457
     458                        // Format page date.
     459                        $page_date = mysql2date("Ymd\TH:i:s", $page->post_date, false);
     460                        $page_date_gmt = mysql2date("Ymd\TH:i:s", $page->post_date_gmt, false);
     461
     462                        // For drafts use the GMT version of the date
     463                        if ( $page->post_status == 'draft' )
     464                                $page_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $page->post_date ), 'Ymd\TH:i:s' );
     465
     466                        // Pull the categories info together.
     467                        $categories = array();
     468                        foreach ( wp_get_post_categories($page->ID) as $cat_id ) {
     469                                $categories[] = get_cat_name($cat_id);
     470                        }
     471
     472                        // Get the author info.
     473                        $author = get_userdata($page->post_author);
     474
     475                        $page_template = get_post_meta( $page->ID, '_wp_page_template', true );
     476                        if ( empty( $page_template ) )
     477                                $page_template = 'default';
     478
     479                        $page_struct = array(
     480                                "dateCreated"                   => new IXR_Date($page_date),
     481                                "userid"                                => $page->post_author,
     482                                "page_id"                               => $page->ID,
     483                                "page_status"                   => $page->post_status,
     484                                "description"                   => $full_page["main"],
     485                                "title"                                 => $page->post_title,
     486                                "link"                                  => $link,
     487                                "permaLink"                             => $link,
     488                                "categories"                    => $categories,
     489                                "excerpt"                               => $page->post_excerpt,
     490                                "text_more"                             => $full_page["extended"],
     491                                "mt_allow_comments"             => $allow_comments,
     492                                "mt_allow_pings"                => $allow_pings,
     493                                "wp_slug"                               => $page->post_name,
     494                                "wp_password"                   => $page->post_password,
     495                                "wp_author"                             => $author->display_name,
     496                                "wp_page_parent_id"             => $page->post_parent,
     497                                "wp_page_parent_title"  => $parent_title,
     498                                "wp_page_order"                 => $page->menu_order,
     499                                "wp_author_id"                  => $author->ID,
     500                                "wp_author_display_name"        => $author->display_name,
     501                                "date_created_gmt"              => new IXR_Date($page_date_gmt),
     502                                "custom_fields"                 => $this->get_custom_fields($page_id),
     503                                "wp_page_template"              => $page_template
     504                        );
     505
     506                        return($page_struct);
     507                }
     508                // If the page doesn't exist indicate that.
     509                else {
     510                        return(new IXR_Error(404, __("Sorry, no such page.")));
     511                }
     512        }
     513
     514        /**
     515         * Retrieve Pages.
     516         *
     517         * @since 2.2.0
     518         *
     519         * @param array $args Method parameters.
     520         * @return array
     521         */
     522        function wp_getPages($args) {
     523                $this->escape($args);
     524
     525                $blog_id        = (int) $args[0];
     526                $username       = $args[1];
     527                $password       = $args[2];
     528                $num_pages      = isset($args[3]) ? (int) $args[3] : 10;
     529
     530                if ( !$user = $this->login($username, $password) )
     531                        return $this->error;
     532
     533                if ( !current_user_can( 'edit_pages' ) )
     534                        return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) );
     535
     536                do_action('xmlrpc_call', 'wp.getPages');
     537
     538                $pages = get_posts( array('post_type' => 'page', 'post_status' => 'any', 'numberposts' => $num_pages) );
     539                $num_pages = count($pages);
     540
     541                // If we have pages, put together their info.
     542                if ( $num_pages >= 1 ) {
     543                        $pages_struct = array();
     544
     545                        for ( $i = 0; $i < $num_pages; $i++ ) {
     546                                $page = wp_xmlrpc_server::wp_getPage(array(
     547                                        $blog_id, $pages[$i]->ID, $username, $password
     548                                ));
     549                                $pages_struct[] = $page;
     550                        }
     551
     552                        return($pages_struct);
     553                }
     554                // If no pages were found return an error.
     555                else {
     556                        return(array());
     557                }
     558        }
     559
     560        /**
     561         * Create new page.
     562         *
     563         * @since 2.2.0
     564         *
     565         * @param array $args Method parameters.
     566         * @return unknown
     567         */
     568        function wp_newPage($args) {
     569                // Items not escaped here will be escaped in newPost.
     570                $username       = $this->escape($args[1]);
     571                $password       = $this->escape($args[2]);
     572                $page           = $args[3];
     573                $publish        = $args[4];
     574
     575                if ( !$user = $this->login($username, $password) )
     576                        return $this->error;
     577
     578                do_action('xmlrpc_call', 'wp.newPage');
     579
     580                // Make sure the user is allowed to add new pages.
     581                if ( !current_user_can("publish_pages") )
     582                        return(new IXR_Error(401, __("Sorry, you cannot add new pages.")));
     583
     584                // Mark this as content for a page.
     585                $args[3]["post_type"] = "page";
     586
     587                // Let mw_newPost do all of the heavy lifting.
     588                return($this->mw_newPost($args));
     589        }
     590
     591        /**
     592         * Delete page.
     593         *
     594         * @since 2.2.0
     595         *
     596         * @param array $args Method parameters.
     597         * @return bool True, if success.
     598         */
     599        function wp_deletePage($args) {
     600                $this->escape($args);
     601
     602                $blog_id        = (int) $args[0];
     603                $username       = $args[1];
     604                $password       = $args[2];
     605                $page_id        = (int) $args[3];
     606
     607                if ( !$user = $this->login($username, $password) )
     608                        return $this->error;
     609
     610                do_action('xmlrpc_call', 'wp.deletePage');
     611
     612                // Get the current page based on the page_id and
     613                // make sure it is a page and not a post.
     614                $actual_page = wp_get_single_post($page_id, ARRAY_A);
     615                if ( !$actual_page || ($actual_page["post_type"] != "page") )
     616                        return(new IXR_Error(404, __("Sorry, no such page.")));
     617
     618                // Make sure the user can delete pages.
     619                if ( !current_user_can("delete_page", $page_id) )
     620                        return(new IXR_Error(401, __("Sorry, you do not have the right to delete this page.")));
     621
     622                // Attempt to delete the page.
     623                $result = wp_delete_post($page_id);
     624                if ( !$result )
     625                        return(new IXR_Error(500, __("Failed to delete the page.")));
     626
     627                return(true);
     628        }
     629
     630        /**
     631         * Edit page.
     632         *
     633         * @since 2.2.0
     634         *
     635         * @param array $args Method parameters.
     636         * @return unknown
     637         */
     638        function wp_editPage($args) {
     639                // Items not escaped here will be escaped in editPost.
     640                $blog_id        = (int) $args[0];
     641                $page_id        = (int) $this->escape($args[1]);
     642                $username       = $this->escape($args[2]);
     643                $password       = $this->escape($args[3]);
     644                $content        = $args[4];
     645                $publish        = $args[5];
     646
     647                if ( !$user = $this->login($username, $password) )
     648                        return $this->error;
     649
     650                do_action('xmlrpc_call', 'wp.editPage');
     651
     652                // Get the page data and make sure it is a page.
     653                $actual_page = wp_get_single_post($page_id, ARRAY_A);
     654                if ( !$actual_page || ($actual_page["post_type"] != "page") )
     655                        return(new IXR_Error(404, __("Sorry, no such page.")));
     656
     657                // Make sure the user is allowed to edit pages.
     658                if ( !current_user_can("edit_page", $page_id) )
     659                        return(new IXR_Error(401, __("Sorry, you do not have the right to edit this page.")));
     660
     661                // Mark this as content for a page.
     662                $content["post_type"] = "page";
     663
     664                // Arrange args in the way mw_editPost understands.
     665                $args = array(
     666                        $page_id,
     667                        $username,
     668                        $password,
     669                        $content,
     670                        $publish
     671                );
     672
     673                // Let mw_editPost do all of the heavy lifting.
     674                return($this->mw_editPost($args));
     675        }
     676
     677        /**
     678         * Retrieve page list.
     679         *
     680         * @since 2.2.0
     681         *
     682         * @param array $args Method parameters.
     683         * @return unknown
     684         */
     685        function wp_getPageList($args) {
     686                global $wpdb;
     687
     688                $this->escape($args);
     689
     690                $blog_id                                = (int) $args[0];
     691                $username                               = $args[1];
     692                $password                               = $args[2];
     693
     694                if ( !$user = $this->login($username, $password) )
     695                        return $this->error;
     696
     697                if ( !current_user_can( 'edit_pages' ) )
     698                        return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) );
     699
     700                do_action('xmlrpc_call', 'wp.getPageList');
     701
     702                // Get list of pages ids and titles
     703                $page_list = $wpdb->get_results("
     704                        SELECT ID page_id,
     705                                post_title page_title,
     706                                post_parent page_parent_id,
     707                                post_date_gmt,
     708                                post_date,
     709                                post_status
     710                        FROM {$wpdb->posts}
     711                        WHERE post_type = 'page'
     712                        ORDER BY ID
     713                ");
     714
     715                // The date needs to be formated properly.
     716                $num_pages = count($page_list);
     717                for ( $i = 0; $i < $num_pages; $i++ ) {
     718                        $post_date = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date, false);
     719                        $post_date_gmt = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date_gmt, false);
     720
     721                        $page_list[$i]->dateCreated = new IXR_Date($post_date);
     722                        $page_list[$i]->date_created_gmt = new IXR_Date($post_date_gmt);
     723
     724                        // For drafts use the GMT version of the date
     725                        if ( $page_list[$i]->post_status == 'draft' ) {
     726                                $page_list[$i]->date_created_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $page_list[$i]->post_date ), 'Ymd\TH:i:s' );
     727                                $page_list[$i]->date_created_gmt = new IXR_Date( $page_list[$i]->date_created_gmt );
     728                        }
     729
     730                        unset($page_list[$i]->post_date_gmt);
     731                        unset($page_list[$i]->post_date);
     732                        unset($page_list[$i]->post_status);
     733                }
     734
     735                return($page_list);
     736        }
     737
     738        /**
     739         * Retrieve authors list.
     740         *
     741         * @since 2.2.0
     742         *
     743         * @param array $args Method parameters.
     744         * @return array
     745         */
     746        function wp_getAuthors($args) {
     747
     748                $this->escape($args);
     749
     750                $blog_id        = (int) $args[0];
     751                $username       = $args[1];
     752                $password       = $args[2];
     753
     754                if ( !$user = $this->login($username, $password) )
     755                        return $this->error;
     756
     757                if ( !current_user_can("edit_posts") )
     758                        return(new IXR_Error(401, __("Sorry, you cannot edit posts on this site.")));
     759
     760                do_action('xmlrpc_call', 'wp.getAuthors');
     761
     762                $authors = array();
     763                foreach ( get_users() as $user_id => $user_object ) {
     764                        $authors[] = array(
     765                                "user_id"       => $user_id,
     766                                "user_login"    => $user_object->user_login,
     767                                "display_name"  => $user_object->display_name
     768                        );
     769                }
     770
     771                return $authors;
     772        }
     773
     774        /**
     775         * Get list of all tags
     776         *
     777         * @since 2.7
     778         *
     779         * @param array $args Method parameters.
     780         * @return array
     781         */
     782        function wp_getTags( $args ) {
     783                $this->escape( $args );
     784
     785                $blog_id                = (int) $args[0];
     786                $username               = $args[1];
     787                $password               = $args[2];
     788
     789                if ( !$user = $this->login($username, $password) )
     790                        return $this->error;
     791
     792                if ( !current_user_can( 'edit_posts' ) )
     793                        return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view tags.' ) );
     794
     795                do_action( 'xmlrpc_call', 'wp.getKeywords' );
     796
     797                $tags = array( );
     798
     799                if ( $all_tags = get_tags() ) {
     800                        foreach( (array) $all_tags as $tag ) {
     801                                $struct['tag_id']                       = $tag->term_id;
     802                                $struct['name']                         = $tag->name;
     803                                $struct['count']                        = $tag->count;
     804                                $struct['slug']                         = $tag->slug;
     805                                $struct['html_url']                     = esc_html( get_tag_link( $tag->term_id ) );
     806                                $struct['rss_url']                      = esc_html( get_tag_feed_link( $tag->term_id ) );
     807
     808                                $tags[] = $struct;
     809                        }
     810                }
     811
     812                return $tags;
     813        }
     814
     815        /**
     816         * Create new category.
     817         *
     818         * @since 2.2.0
     819         *
     820         * @param array $args Method parameters.
     821         * @return int Category ID.
     822         */
     823        function wp_newCategory($args) {
     824                $this->escape($args);
     825
     826                $blog_id                                = (int) $args[0];
     827                $username                               = $args[1];
     828                $password                               = $args[2];
     829                $category                               = $args[3];
     830
     831                if ( !$user = $this->login($username, $password) )
     832                        return $this->error;
     833
     834                do_action('xmlrpc_call', 'wp.newCategory');
     835
     836                // Make sure the user is allowed to add a category.
     837                if ( !current_user_can("manage_categories") )
     838                        return(new IXR_Error(401, __("Sorry, you do not have the right to add a category.")));
     839
     840                // If no slug was provided make it empty so that
     841                // WordPress will generate one.
     842                if ( empty($category["slug"]) )
     843                        $category["slug"] = "";
     844
     845                // If no parent_id was provided make it empty
     846                // so that it will be a top level page (no parent).
     847                if ( !isset($category["parent_id"]) )
     848                        $category["parent_id"] = "";
     849
     850                // If no description was provided make it empty.
     851                if ( empty($category["description"]) )
     852                        $category["description"] = "";
     853
     854                $new_category = array(
     855                        "cat_name"                              => $category["name"],
     856                        "category_nicename"             => $category["slug"],
     857                        "category_parent"               => $category["parent_id"],
     858                        "category_description"  => $category["description"]
     859                );
     860
     861                $cat_id = wp_insert_category($new_category, true);
     862                if ( is_wp_error( $cat_id ) ) {
     863                        if ( 'term_exists' == $cat_id->get_error_code() )
     864                                return (int) $cat_id->get_error_data();
     865                        else
     866                                return(new IXR_Error(500, __("Sorry, the new category failed.")));
     867                } elseif ( ! $cat_id ) {
     868                        return(new IXR_Error(500, __("Sorry, the new category failed.")));
     869                }
     870
     871                return($cat_id);
     872        }
     873
     874        /**
     875         * Remove category.
     876         *
     877         * @since 2.5.0
     878         *
     879         * @param array $args Method parameters.
     880         * @return mixed See {@link wp_delete_category()} for return info.
     881         */
     882        function wp_deleteCategory($args) {
     883                $this->escape($args);
     884
     885                $blog_id                = (int) $args[0];
     886                $username               = $args[1];
     887                $password               = $args[2];
     888                $category_id    = (int) $args[3];
     889
     890                if ( !$user = $this->login($username, $password) )
     891                        return $this->error;
     892
     893                do_action('xmlrpc_call', 'wp.deleteCategory');
     894
     895                if ( !current_user_can("manage_categories") )
     896                        return new IXR_Error( 401, __( "Sorry, you do not have the right to delete a category." ) );
     897
     898                return wp_delete_category( $category_id );
     899        }
     900
     901        /**
     902         * Retrieve category list.
     903         *
     904         * @since 2.2.0
     905         *
     906         * @param array $args Method parameters.
     907         * @return array
     908         */
     909        function wp_suggestCategories($args) {
     910                $this->escape($args);
     911
     912                $blog_id                                = (int) $args[0];
     913                $username                               = $args[1];
     914                $password                               = $args[2];
     915                $category                               = $args[3];
     916                $max_results                    = (int) $args[4];
     917
     918                if ( !$user = $this->login($username, $password) )
     919                        return $this->error;
     920
     921                if ( !current_user_can( 'edit_posts' ) )
     922                        return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts to this site in order to view categories.' ) );
     923
     924                do_action('xmlrpc_call', 'wp.suggestCategories');
     925
     926                $category_suggestions = array();
     927                $args = array('get' => 'all', 'number' => $max_results, 'name__like' => $category);
     928                foreach ( (array) get_categories($args) as $cat ) {
     929                        $category_suggestions[] = array(
     930                                "category_id"   => $cat->cat_ID,
     931                                "category_name" => $cat->cat_name
     932                        );
     933                }
     934
     935                return($category_suggestions);
     936        }
     937
     938        /**
     939         * Retrieve comment.
     940         *
     941         * @since 2.7.0
     942         *
     943         * @param array $args Method parameters.
     944         * @return array
     945         */
     946        function wp_getComment($args) {
     947                $this->escape($args);
     948
     949                $blog_id        = (int) $args[0];
     950                $username       = $args[1];
     951                $password       = $args[2];
     952                $comment_id     = (int) $args[3];
     953
     954                if ( !$user = $this->login($username, $password) )
     955                        return $this->error;
     956
     957                if ( !current_user_can( 'moderate_comments' ) )
     958                        return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
     959
     960                do_action('xmlrpc_call', 'wp.getComment');
     961
     962                if ( ! $comment = get_comment($comment_id) )
     963                        return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
     964
     965                // Format page date.
     966                $comment_date = mysql2date("Ymd\TH:i:s", $comment->comment_date, false);
     967                $comment_date_gmt = mysql2date("Ymd\TH:i:s", $comment->comment_date_gmt, false);
     968
     969                if ( '0' == $comment->comment_approved )
     970                        $comment_status = 'hold';
     971                else if ( 'spam' == $comment->comment_approved )
     972                        $comment_status = 'spam';
     973                else if ( '1' == $comment->comment_approved )
     974                        $comment_status = 'approve';
     975                else
     976                        $comment_status = $comment->comment_approved;
     977
     978                $link = get_comment_link($comment);
     979
     980                $comment_struct = array(
     981                        "date_created_gmt"              => new IXR_Date($comment_date_gmt),
     982                        "user_id"                               => $comment->user_id,
     983                        "comment_id"                    => $comment->comment_ID,
     984                        "parent"                                => $comment->comment_parent,
     985                        "status"                                => $comment_status,
     986                        "content"                               => $comment->comment_content,
     987                        "link"                                  => $link,
     988                        "post_id"                               => $comment->comment_post_ID,
     989                        "post_title"                    => get_the_title($comment->comment_post_ID),
     990                        "author"                                => $comment->comment_author,
     991                        "author_url"                    => $comment->comment_author_url,
     992                        "author_email"                  => $comment->comment_author_email,
     993                        "author_ip"                             => $comment->comment_author_IP,
     994                        "type"                                  => $comment->comment_type,
     995                );
     996
     997                return $comment_struct;
     998        }
     999
     1000        /**
     1001         * Retrieve comments.
     1002         *
     1003         * @since 2.7.0
     1004         *
     1005         * @param array $args Method parameters.
     1006         * @return array
     1007         */
     1008        function wp_getComments($args) {
     1009                $raw_args = $args;
     1010                $this->escape($args);
     1011
     1012                $blog_id        = (int) $args[0];
     1013                $username       = $args[1];
     1014                $password       = $args[2];
     1015                $struct         = $args[3];
     1016
     1017                if ( !$user = $this->login($username, $password) )
     1018                        return $this->error;
     1019
     1020                if ( !current_user_can( 'moderate_comments' ) )
     1021                        return new IXR_Error( 401, __( 'Sorry, you cannot edit comments.' ) );
     1022
     1023                do_action('xmlrpc_call', 'wp.getComments');
     1024
     1025                if ( isset($struct['status']) )
     1026                        $status = $struct['status'];
     1027                else
     1028                        $status = '';
     1029
     1030                $post_id = '';
     1031                if ( isset($struct['post_id']) )
     1032                        $post_id = absint($struct['post_id']);
     1033
     1034                $offset = 0;
     1035                if ( isset($struct['offset']) )
     1036                        $offset = absint($struct['offset']);
     1037
     1038                $number = 10;
     1039                if ( isset($struct['number']) )
     1040                        $number = absint($struct['number']);
     1041
     1042                $comments = get_comments( array('status' => $status, 'post_id' => $post_id, 'offset' => $offset, 'number' => $number ) );
     1043                $num_comments = count($comments);
     1044
     1045                if ( ! $num_comments )
     1046                        return array();
     1047
     1048                $comments_struct = array();
     1049
     1050                for ( $i = 0; $i < $num_comments; $i++ ) {
     1051                        $comment = wp_xmlrpc_server::wp_getComment(array(
     1052                                $raw_args[0], $raw_args[1], $raw_args[2], $comments[$i]->comment_ID,
     1053                        ));
     1054                        $comments_struct[] = $comment;
     1055                }
     1056
     1057                return $comments_struct;
     1058        }
     1059
     1060        /**
     1061         * Remove comment.
     1062         *
     1063         * @since 2.7.0
     1064         *
     1065         * @param array $args Method parameters.
     1066         * @return mixed {@link wp_delete_comment()}
     1067         */
     1068        function wp_deleteComment($args) {
     1069                $this->escape($args);
     1070
     1071                $blog_id        = (int) $args[0];
     1072                $username       = $args[1];
     1073                $password       = $args[2];
     1074                $comment_ID     = (int) $args[3];
     1075
     1076                if ( !$user = $this->login($username, $password) )
     1077                        return $this->error;
     1078
     1079                if ( !current_user_can( 'moderate_comments' ) )
     1080                        return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
     1081
     1082                do_action('xmlrpc_call', 'wp.deleteComment');
     1083
     1084                if ( ! get_comment($comment_ID) )
     1085                        return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
     1086
     1087                return wp_delete_comment($comment_ID);
     1088        }
     1089
     1090        /**
     1091         * Edit comment.
     1092         *
     1093         * @since 2.7.0
     1094         *
     1095         * @param array $args Method parameters.
     1096         * @return bool True, on success.
     1097         */
     1098        function wp_editComment($args) {
     1099                $this->escape($args);
     1100
     1101                $blog_id        = (int) $args[0];
     1102                $username       = $args[1];
     1103                $password       = $args[2];
     1104                $comment_ID     = (int) $args[3];
     1105                $content_struct = $args[4];
     1106
     1107                if ( !$user = $this->login($username, $password) )
     1108                        return $this->error;
     1109
     1110                if ( !current_user_can( 'moderate_comments' ) )
     1111                        return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
     1112
     1113                do_action('xmlrpc_call', 'wp.editComment');
     1114
     1115                if ( ! get_comment($comment_ID) )
     1116                        return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
     1117
     1118                if ( isset($content_struct['status']) ) {
     1119                        $statuses = get_comment_statuses();
     1120                        $statuses = array_keys($statuses);
     1121
     1122                        if ( ! in_array($content_struct['status'], $statuses) )
     1123                                return new IXR_Error( 401, __( 'Invalid comment status.' ) );
     1124                        $comment_approved = $content_struct['status'];
     1125                }
     1126
     1127                // Do some timestamp voodoo
     1128                if ( !empty( $content_struct['date_created_gmt'] ) ) {
     1129                        $dateCreated = str_replace( 'Z', '', $content_struct['date_created_gmt']->getIso() ) . 'Z'; // We know this is supposed to be GMT, so we're going to slap that Z on there by force
     1130                        $comment_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
     1131                        $comment_date_gmt = iso8601_to_datetime($dateCreated, GMT);
     1132                }
     1133
     1134                if ( isset($content_struct['content']) )
     1135                        $comment_content = $content_struct['content'];
     1136
     1137                if ( isset($content_struct['author']) )
     1138                        $comment_author = $content_struct['author'];
     1139
     1140                if ( isset($content_struct['author_url']) )
     1141                        $comment_author_url = $content_struct['author_url'];
     1142
     1143                if ( isset($content_struct['author_email']) )
     1144                        $comment_author_email = $content_struct['author_email'];
     1145
     1146                // We've got all the data -- post it:
     1147                $comment = compact('comment_ID', 'comment_content', 'comment_approved', 'comment_date', 'comment_date_gmt', 'comment_author', 'comment_author_email', 'comment_author_url');
     1148
     1149                $result = wp_update_comment($comment);
     1150                if ( is_wp_error( $result ) )
     1151                        return new IXR_Error(500, $result->get_error_message());
     1152
     1153                if ( !$result )
     1154                        return new IXR_Error(500, __('Sorry, the comment could not be edited. Something wrong happened.'));
     1155
     1156                return true;
     1157        }
     1158
     1159        /**
     1160         * Create new comment.
     1161         *
     1162         * @since 2.7.0
     1163         *
     1164         * @param array $args Method parameters.
     1165         * @return mixed {@link wp_new_comment()}
     1166         */
     1167        function wp_newComment($args) {
     1168                global $wpdb;
     1169
     1170                $this->escape($args);
     1171
     1172                $blog_id        = (int) $args[0];
     1173                $username       = $args[1];
     1174                $password       = $args[2];
     1175                $post           = $args[3];
     1176                $content_struct = $args[4];
     1177
     1178                $allow_anon = apply_filters('xmlrpc_allow_anonymous_comments', false);
     1179
     1180                $user = $this->login($username, $password);
     1181
     1182                if ( !$user ) {
     1183                        $logged_in = false;
     1184                        if ( $allow_anon && get_option('comment_registration') )
     1185                                return new IXR_Error( 403, __( 'You must be registered to comment' ) );
     1186                        else if ( !$allow_anon )
     1187                                return $this->error;
     1188                } else {
     1189                        $logged_in = true;
     1190                }
     1191
     1192                if ( is_numeric($post) )
     1193                        $post_id = absint($post);
     1194                else
     1195                        $post_id = url_to_postid($post);
     1196
     1197                if ( ! $post_id )
     1198                        return new IXR_Error( 404, __( 'Invalid post ID.' ) );
     1199
     1200                if ( ! get_post($post_id) )
     1201                        return new IXR_Error( 404, __( 'Invalid post ID.' ) );
     1202
     1203                $comment['comment_post_ID'] = $post_id;
     1204
     1205                if ( $logged_in ) {
     1206                        $comment['comment_author'] = $wpdb->escape( $user->display_name );
     1207                        $comment['comment_author_email'] = $wpdb->escape( $user->user_email );
     1208                        $comment['comment_author_url'] = $wpdb->escape( $user->user_url );
     1209                        $comment['user_ID'] = $user->ID;
     1210                } else {
     1211                        $comment['comment_author'] = '';
     1212                        if ( isset($content_struct['author']) )
     1213                                $comment['comment_author'] = $content_struct['author'];
     1214
     1215                        $comment['comment_author_email'] = '';
     1216                        if ( isset($content_struct['author_email']) )
     1217                                $comment['comment_author_email'] = $content_struct['author_email'];
     1218
     1219                        $comment['comment_author_url'] = '';
     1220                        if ( isset($content_struct['author_url']) )
     1221                                $comment['comment_author_url'] = $content_struct['author_url'];
     1222
     1223                        $comment['user_ID'] = 0;
     1224
     1225                        if ( get_option('require_name_email') ) {
     1226                                if ( 6 > strlen($comment['comment_author_email']) || '' == $comment['comment_author'] )
     1227                                        return new IXR_Error( 403, __( 'Comment author name and email are required' ) );
     1228                                elseif ( !is_email($comment['comment_author_email']) )
     1229                                        return new IXR_Error( 403, __( 'A valid email address is required' ) );
     1230                        }
     1231                }
     1232
     1233                $comment['comment_parent'] = isset($content_struct['comment_parent']) ? absint($content_struct['comment_parent']) : 0;
     1234
     1235                $comment['comment_content'] = $content_struct['content'];
     1236
     1237                do_action('xmlrpc_call', 'wp.newComment');
     1238
     1239                return wp_new_comment($comment);
     1240        }
     1241
     1242        /**
     1243         * Retrieve all of the comment status.
     1244         *
     1245         * @since 2.7.0
     1246         *
     1247         * @param array $args Method parameters.
     1248         * @return array
     1249         */
     1250        function wp_getCommentStatusList($args) {
     1251                $this->escape( $args );
     1252
     1253                $blog_id        = (int) $args[0];
     1254                $username       = $args[1];
     1255                $password       = $args[2];
     1256
     1257                if ( !$user = $this->login($username, $password) )
     1258                        return $this->error;
     1259
     1260                if ( !current_user_can( 'moderate_comments' ) )
     1261                        return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
     1262
     1263                do_action('xmlrpc_call', 'wp.getCommentStatusList');
     1264
     1265                return get_comment_statuses( );
     1266        }
     1267
     1268        /**
     1269         * Retrieve comment count.
     1270         *
     1271         * @since 2.5.0
     1272         *
     1273         * @param array $args Method parameters.
     1274         * @return array
     1275         */
     1276        function wp_getCommentCount( $args ) {
     1277                $this->escape($args);
     1278
     1279                $blog_id        = (int) $args[0];
     1280                $username       = $args[1];
     1281                $password       = $args[2];
     1282                $post_id        = (int) $args[3];
     1283
     1284                if ( !$user = $this->login($username, $password) )
     1285                        return $this->error;
     1286
     1287                if ( !current_user_can( 'edit_posts' ) )
     1288                        return new IXR_Error( 403, __( 'You are not allowed access to details about comments.' ) );
     1289
     1290                do_action('xmlrpc_call', 'wp.getCommentCount');
     1291
     1292                $count = wp_count_comments( $post_id );
     1293                return array(
     1294                        "approved" => $count->approved,
     1295                        "awaiting_moderation" => $count->moderated,
     1296                        "spam" => $count->spam,
     1297                        "total_comments" => $count->total_comments
     1298                );
     1299        }
     1300
     1301        /**
     1302         * Retrieve post statuses.
     1303         *
     1304         * @since 2.5.0
     1305         *
     1306         * @param array $args Method parameters.
     1307         * @return array
     1308         */
     1309        function wp_getPostStatusList( $args ) {
     1310                $this->escape( $args );
     1311
     1312                $blog_id        = (int) $args[0];
     1313                $username       = $args[1];
     1314                $password       = $args[2];
     1315
     1316                if ( !$user = $this->login($username, $password) )
     1317                        return $this->error;
     1318
     1319                if ( !current_user_can( 'edit_posts' ) )
     1320                        return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
     1321
     1322                do_action('xmlrpc_call', 'wp.getPostStatusList');
     1323
     1324                return get_post_statuses( );
     1325        }
     1326
     1327        /**
     1328         * Retrieve page statuses.
     1329         *
     1330         * @since 2.5.0
     1331         *
     1332         * @param array $args Method parameters.
     1333         * @return array
     1334         */
     1335        function wp_getPageStatusList( $args ) {
     1336                $this->escape( $args );
     1337
     1338                $blog_id        = (int) $args[0];
     1339                $username       = $args[1];
     1340                $password       = $args[2];
     1341
     1342                if ( !$user = $this->login($username, $password) )
     1343                        return $this->error;
     1344
     1345                if ( !current_user_can( 'edit_posts' ) )
     1346                        return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
     1347
     1348                do_action('xmlrpc_call', 'wp.getPageStatusList');
     1349
     1350                return get_page_statuses( );
     1351        }
     1352
     1353        /**
     1354         * Retrieve page templates.
     1355         *
     1356         * @since 2.6.0
     1357         *
     1358         * @param array $args Method parameters.
     1359         * @return array
     1360         */
     1361        function wp_getPageTemplates( $args ) {
     1362                $this->escape( $args );
     1363
     1364                $blog_id        = (int) $args[0];
     1365                $username       = $args[1];
     1366                $password       = $args[2];
     1367
     1368                if ( !$user = $this->login($username, $password) )
     1369                        return $this->error;
     1370
     1371                if ( !current_user_can( 'edit_pages' ) )
     1372                        return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
     1373
     1374                $templates = get_page_templates( );
     1375                $templates['Default'] = 'default';
     1376
     1377                return $templates;
     1378        }
     1379
     1380        /**
     1381         * Retrieve blog options.
     1382         *
     1383         * @since 2.6.0
     1384         *
     1385         * @param array $args Method parameters.
     1386         * @return array
     1387         */
     1388        function wp_getOptions( $args ) {
     1389                $this->escape( $args );
     1390
     1391                $blog_id        = (int) $args[0];
     1392                $username       = $args[1];
     1393                $password       = $args[2];
     1394                $options        = (array) $args[3];
     1395
     1396                if ( !$user = $this->login($username, $password) )
     1397                        return $this->error;
     1398
     1399                // If no specific options where asked for, return all of them
     1400                if ( count( $options ) == 0 )
     1401                        $options = array_keys($this->blog_options);
     1402
     1403                return $this->_getOptions($options);
     1404        }
     1405
     1406        /**
     1407         * Retrieve blog options value from list.
     1408         *
     1409         * @since 2.6.0
     1410         *
     1411         * @param array $options Options to retrieve.
     1412         * @return array
     1413         */
     1414        function _getOptions($options) {
     1415                $data = array( );
     1416                foreach ( $options as $option ) {
     1417                        if ( array_key_exists( $option, $this->blog_options ) ) {
     1418                                $data[$option] = $this->blog_options[$option];
     1419                                //Is the value static or dynamic?
     1420                                if ( isset( $data[$option]['option'] ) ) {
     1421                                        $data[$option]['value'] = get_option( $data[$option]['option'] );
     1422                                        unset($data[$option]['option']);
     1423                                }
     1424                        }
     1425                }
     1426
     1427                return $data;
     1428        }
     1429
     1430        /**
     1431         * Update blog options.
     1432         *
     1433         * @since 2.6.0
     1434         *
     1435         * @param array $args Method parameters.
     1436         * @return unknown
     1437         */
     1438        function wp_setOptions( $args ) {
     1439                $this->escape( $args );
     1440
     1441                $blog_id        = (int) $args[0];
     1442                $username       = $args[1];
     1443                $password       = $args[2];
     1444                $options        = (array) $args[3];
     1445
     1446                if ( !$user = $this->login($username, $password) )
     1447                        return $this->error;
     1448
     1449                if ( !current_user_can( 'manage_options' ) )
     1450                        return new IXR_Error( 403, __( 'You are not allowed to update options.' ) );
     1451
     1452                foreach ( $options as $o_name => $o_value ) {
     1453                        $option_names[] = $o_name;
     1454                        if ( !array_key_exists( $o_name, $this->blog_options ) )
     1455                                continue;
     1456
     1457                        if ( $this->blog_options[$o_name]['readonly'] == true )
     1458                                continue;
     1459
     1460                        update_option( $this->blog_options[$o_name]['option'], $o_value );
     1461                }
     1462
     1463                //Now return the updated values
     1464                return $this->_getOptions($option_names);
     1465        }
     1466
     1467        /* Blogger API functions.
     1468         * specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/
     1469         */
     1470
     1471        /**
     1472         * Retrieve blogs that user owns.
     1473         *
     1474         * Will make more sense once we support multiple blogs.
     1475         *
     1476         * @since 1.5.0
     1477         *
     1478         * @param array $args Method parameters.
     1479         * @return array
     1480         */
     1481        function blogger_getUsersBlogs($args) {
     1482                if ( is_multisite() )
     1483                        return $this->_multisite_getUsersBlogs($args);
     1484
     1485                $this->escape($args);
     1486
     1487                $username = $args[1];
     1488                $password  = $args[2];
     1489
     1490                if ( !$user = $this->login($username, $password) )
     1491                        return $this->error;
     1492
     1493                do_action('xmlrpc_call', 'blogger.getUsersBlogs');
     1494
     1495                $is_admin = current_user_can('manage_options');
     1496
     1497                $struct = array(
     1498                        'isAdmin'  => $is_admin,
     1499                        'url'      => get_option('home') . '/',
     1500                        'blogid'   => '1',
     1501                        'blogName' => get_option('blogname'),
     1502                        'xmlrpc'   => site_url( 'xmlrpc.php' )
     1503                );
     1504
     1505                return array($struct);
     1506        }
     1507
     1508        /**
     1509         * Private function for retrieving a users blogs for multisite setups
     1510         *
     1511         * @access protected
     1512         */
     1513        function _multisite_getUsersBlogs($args) {
     1514                global $current_blog;
     1515                $domain = $current_blog->domain;
     1516                $path = $current_blog->path . 'xmlrpc.php';
     1517                $protocol = is_ssl() ? 'https' : 'http';
     1518
     1519                $rpc = new IXR_Client("$protocol://{$domain}{$path}");
     1520                $rpc->query('wp.getUsersBlogs', $args[1], $args[2]);
     1521                $blogs = $rpc->getResponse();
     1522
     1523                if ( isset($blogs['faultCode']) )
     1524                        return new IXR_Error($blogs['faultCode'], $blogs['faultString']);
     1525
     1526                if ( $_SERVER['HTTP_HOST'] == $domain && $_SERVER['REQUEST_URI'] == $path ) {
     1527                        return $blogs;
     1528                } else {
     1529                        foreach ( (array) $blogs as $blog ) {
     1530                                if ( strpos($blog['url'], $_SERVER['HTTP_HOST']) )
     1531                                        return array($blog);
     1532                        }
     1533                        return array();
     1534                }
     1535        }
     1536
     1537        /**
     1538         * Retrieve user's data.
     1539         *
     1540         * Gives your client some info about you, so you don't have to.
     1541         *
     1542         * @since 1.5.0
     1543         *
     1544         * @param array $args Method parameters.
     1545         * @return array
     1546         */
     1547        function blogger_getUserInfo($args) {
     1548
     1549                $this->escape($args);
     1550
     1551                $username = $args[1];
     1552                $password  = $args[2];
     1553
     1554                if ( !$user = $this->login($username, $password) )
     1555                        return $this->error;
     1556
     1557                if ( !current_user_can( 'edit_posts' ) )
     1558                        return new IXR_Error( 401, __( 'Sorry, you do not have access to user data on this site.' ) );
     1559
     1560                do_action('xmlrpc_call', 'blogger.getUserInfo');
     1561
     1562                $struct = array(
     1563                        'nickname'  => $user->nickname,
     1564                        'userid'    => $user->ID,
     1565                        'url'       => $user->user_url,
     1566                        'lastname'  => $user->last_name,
     1567                        'firstname' => $user->first_name
     1568                );
     1569
     1570                return $struct;
     1571        }
     1572
     1573        /**
     1574         * Retrieve post.
     1575         *
     1576         * @since 1.5.0
     1577         *
     1578         * @param array $args Method parameters.
     1579         * @return array
     1580         */
     1581        function blogger_getPost($args) {
     1582
     1583                $this->escape($args);
     1584
     1585                $post_ID    = (int) $args[1];
     1586                $username = $args[2];
     1587                $password  = $args[3];
     1588
     1589                if ( !$user = $this->login($username, $password) )
     1590                        return $this->error;
     1591
     1592                if ( !current_user_can( 'edit_post', $post_ID ) )
     1593                        return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
     1594
     1595                do_action('xmlrpc_call', 'blogger.getPost');
     1596
     1597                $post_data = wp_get_single_post($post_ID, ARRAY_A);
     1598
     1599                $categories = implode(',', wp_get_post_categories($post_ID));
     1600
     1601                $content  = '<title>'.stripslashes($post_data['post_title']).'</title>';
     1602                $content .= '<category>'.$categories.'</category>';
     1603                $content .= stripslashes($post_data['post_content']);
     1604
     1605                $struct = array(
     1606                        'userid'    => $post_data['post_author'],
     1607                        'dateCreated' => new IXR_Date(mysql2date('Ymd\TH:i:s', $post_data['post_date'], false)),
     1608                        'content'     => $content,
     1609                        'postid'  => $post_data['ID']
     1610                );
     1611
     1612                return $struct;
     1613        }
     1614
     1615        /**
     1616         * Retrieve list of recent posts.
     1617         *
     1618         * @since 1.5.0
     1619         *
     1620         * @param array $args Method parameters.
     1621         * @return array
     1622         */
     1623        function blogger_getRecentPosts($args) {
     1624
     1625                $this->escape($args);
     1626
     1627                $blog_ID    = (int) $args[1]; /* though we don't use it yet */
     1628                $username = $args[2];
     1629                $password  = $args[3];
     1630                $num_posts  = $args[4];
     1631
     1632                if ( !$user = $this->login($username, $password) )
     1633                        return $this->error;
     1634
     1635                do_action('xmlrpc_call', 'blogger.getRecentPosts');
     1636
     1637                $posts_list = wp_get_recent_posts($num_posts);
     1638
     1639                if ( !$posts_list ) {
     1640                        $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
     1641                        return $this->error;
     1642                }
     1643
     1644                foreach ($posts_list as $entry) {
     1645                        if ( !current_user_can( 'edit_post', $entry['ID'] ) )
     1646                                continue;
     1647
     1648                        $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false);
     1649                        $categories = implode(',', wp_get_post_categories($entry['ID']));
     1650
     1651                        $content  = '<title>'.stripslashes($entry['post_title']).'</title>';
     1652                        $content .= '<category>'.$categories.'</category>';
     1653                        $content .= stripslashes($entry['post_content']);
     1654
     1655                        $struct[] = array(
     1656                                'userid' => $entry['post_author'],
     1657                                'dateCreated' => new IXR_Date($post_date),
     1658                                'content' => $content,
     1659                                'postid' => $entry['ID'],
     1660                        );
     1661
     1662                }
     1663
     1664                $recent_posts = array();
     1665                for ( $j=0; $j<count($struct); $j++ ) {
     1666                        array_push($recent_posts, $struct[$j]);
     1667                }
     1668
     1669                return $recent_posts;
     1670        }
     1671
     1672        /**
     1673         * Retrieve blog_filename content.
     1674         *
     1675         * @since 1.5.0
     1676         *
     1677         * @param array $args Method parameters.
     1678         * @return string
     1679         */
     1680        function blogger_getTemplate($args) {
     1681
     1682                $this->escape($args);
     1683
     1684                $blog_ID    = (int) $args[1];
     1685                $username = $args[2];
     1686                $password  = $args[3];
     1687                $template   = $args[4]; /* could be 'main' or 'archiveIndex', but we don't use it */
     1688
     1689                if ( !$user = $this->login($username, $password) )
     1690                        return $this->error;
     1691
     1692                do_action('xmlrpc_call', 'blogger.getTemplate');
     1693
     1694                if ( !current_user_can('edit_themes') )
     1695                        return new IXR_Error(401, __('Sorry, this user can not edit the template.'));
     1696
     1697                /* warning: here we make the assumption that the blog's URL is on the same server */
     1698                $filename = get_option('home') . '/';
     1699                $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);
     1700
     1701                $f = fopen($filename, 'r');
     1702                $content = fread($f, filesize($filename));
     1703                fclose($f);
     1704
     1705                /* so it is actually editable with a windows/mac client */
     1706                // 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);
     1707
     1708                return $content;
     1709        }
     1710
     1711        /**
     1712         * Updates the content of blog_filename.
     1713         *
     1714         * @since 1.5.0
     1715         *
     1716         * @param array $args Method parameters.
     1717         * @return bool True when done.
     1718         */
     1719        function blogger_setTemplate($args) {
     1720
     1721                $this->escape($args);
     1722
     1723                $blog_ID    = (int) $args[1];
     1724                $username = $args[2];
     1725                $password  = $args[3];
     1726                $content    = $args[4];
     1727                $template   = $args[5]; /* could be 'main' or 'archiveIndex', but we don't use it */
     1728
     1729                if ( !$user = $this->login($username, $password) )
     1730                        return $this->error;
     1731
     1732                do_action('xmlrpc_call', 'blogger.setTemplate');
     1733
     1734                if ( !current_user_can('edit_themes') )
     1735                        return new IXR_Error(401, __('Sorry, this user cannot edit the template.'));
     1736
     1737                /* warning: here we make the assumption that the blog's URL is on the same server */
     1738                $filename = get_option('home') . '/';
     1739                $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);
     1740
     1741                if ($f = fopen($filename, 'w+')) {
     1742                        fwrite($f, $content);
     1743                        fclose($f);
     1744                } else {
     1745                        return new IXR_Error(500, __('Either the file is not writable, or something wrong happened. The file has not been updated.'));
     1746                }
     1747
     1748                return true;
     1749        }
     1750
     1751        /**
     1752         * Create new post.
     1753         *
     1754         * @since 1.5.0
     1755         *
     1756         * @param array $args Method parameters.
     1757         * @return int
     1758         */
     1759        function blogger_newPost($args) {
     1760
     1761                $this->escape($args);
     1762
     1763                $blog_ID    = (int) $args[1]; /* though we don't use it yet */
     1764                $username = $args[2];
     1765                $password  = $args[3];
     1766                $content    = $args[4];
     1767                $publish    = $args[5];
     1768
     1769                if ( !$user = $this->login($username, $password) )
     1770                        return $this->error;
     1771
     1772                do_action('xmlrpc_call', 'blogger.newPost');
     1773
     1774                $cap = ($publish) ? 'publish_posts' : 'edit_posts';
     1775                if ( !current_user_can($cap) )
     1776                        return new IXR_Error(401, __('Sorry, you are not allowed to post on this site.'));
     1777
     1778                $post_status = ($publish) ? 'publish' : 'draft';
     1779
     1780                $post_author = $user->ID;
     1781
     1782                $post_title = xmlrpc_getposttitle($content);
     1783                $post_category = xmlrpc_getpostcategory($content);
     1784                $post_content = xmlrpc_removepostdata($content);
     1785
     1786                $post_date = current_time('mysql');
     1787                $post_date_gmt = current_time('mysql', 1);
     1788
     1789                $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status');
     1790
     1791                $post_ID = wp_insert_post($post_data);
     1792                if ( is_wp_error( $post_ID ) )
     1793                        return new IXR_Error(500, $post_ID->get_error_message());
     1794
     1795                if ( !$post_ID )
     1796                        return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.'));
     1797
     1798                $this->attach_uploads( $post_ID, $post_content );
     1799
     1800                logIO('O', "Posted ! ID: $post_ID");
     1801
     1802                return $post_ID;
     1803        }
     1804
     1805        /**
     1806         * Edit a post.
     1807         *
     1808         * @since 1.5.0
     1809         *
     1810         * @param array $args Method parameters.
     1811         * @return bool true when done.
     1812         */
     1813        function blogger_editPost($args) {
     1814
     1815                $this->escape($args);
     1816
     1817                $post_ID     = (int) $args[1];
     1818                $username  = $args[2];
     1819                $password   = $args[3];
     1820                $content     = $args[4];
     1821                $publish     = $args[5];
     1822
     1823                if ( !$user = $this->login($username, $password) )
     1824                        return $this->error;
     1825
     1826                do_action('xmlrpc_call', 'blogger.editPost');
     1827
     1828                $actual_post = wp_get_single_post($post_ID,ARRAY_A);
     1829
     1830                if ( !$actual_post || $actual_post['post_type'] != 'post' )
     1831                        return new IXR_Error(404, __('Sorry, no such post.'));
     1832
     1833                $this->escape($actual_post);
     1834
     1835                if ( !current_user_can('edit_post', $post_ID) )
     1836                        return new IXR_Error(401, __('Sorry, you do not have the right to edit this post.'));
     1837
     1838                extract($actual_post, EXTR_SKIP);
     1839
     1840                if ( ('publish' == $post_status) && !current_user_can('publish_posts') )
     1841                        return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.'));
     1842
     1843                $post_title = xmlrpc_getposttitle($content);
     1844                $post_category = xmlrpc_getpostcategory($content);
     1845                $post_content = xmlrpc_removepostdata($content);
     1846
     1847                $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt');
     1848
     1849                $result = wp_update_post($postdata);
     1850
     1851                if ( !$result )
     1852                        return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be edited.'));
     1853
     1854                $this->attach_uploads( $ID, $post_content );
     1855
     1856                return true;
     1857        }
     1858
     1859        /**
     1860         * Remove a post.
     1861         *
     1862         * @since 1.5.0
     1863         *
     1864         * @param array $args Method parameters.
     1865         * @return bool True when post is deleted.
     1866         */
     1867        function blogger_deletePost($args) {
     1868                $this->escape($args);
     1869
     1870                $post_ID     = (int) $args[1];
     1871                $username  = $args[2];
     1872                $password   = $args[3];
     1873                $publish     = $args[4];
     1874
     1875                if ( !$user = $this->login($username, $password) )
     1876                        return $this->error;
     1877
     1878                do_action('xmlrpc_call', 'blogger.deletePost');
     1879
     1880                $actual_post = wp_get_single_post($post_ID,ARRAY_A);
     1881
     1882                if ( !$actual_post || $actual_post['post_type'] != 'post' )
     1883                        return new IXR_Error(404, __('Sorry, no such post.'));
     1884
     1885                if ( !current_user_can('edit_post', $post_ID) )
     1886                        return new IXR_Error(401, __('Sorry, you do not have the right to delete this post.'));
     1887
     1888                $result = wp_delete_post($post_ID);
     1889
     1890                if ( !$result )
     1891                        return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be deleted.'));
     1892
     1893                return true;
     1894        }
     1895
     1896        /* MetaWeblog API functions
     1897         * specs on wherever Dave Winer wants them to be
     1898         */
     1899
     1900        /**
     1901         * Create a new post.
     1902         *
     1903         * @since 1.5.0
     1904         *
     1905         * @param array $args Method parameters.
     1906         * @return int
     1907         */
     1908        function mw_newPost($args) {
     1909                $this->escape($args);
     1910
     1911                $blog_ID     = (int) $args[0]; // we will support this in the near future
     1912                $username  = $args[1];
     1913                $password   = $args[2];
     1914                $content_struct = $args[3];
     1915                $publish     = $args[4];
     1916
     1917                if ( !$user = $this->login($username, $password) )
     1918                        return $this->error;
     1919
     1920                do_action('xmlrpc_call', 'metaWeblog.newPost');
     1921
     1922                $cap = ( $publish ) ? 'publish_posts' : 'edit_posts';
     1923                $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
     1924                $post_type = 'post';
     1925                $page_template = '';
     1926                if ( !empty( $content_struct['post_type'] ) ) {
     1927                        if ( $content_struct['post_type'] == 'page' ) {
     1928                                $cap = ( $publish ) ? 'publish_pages' : 'edit_pages';
     1929                                $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' );
     1930                                $post_type = 'page';
     1931                                if ( !empty( $content_struct['wp_page_template'] ) )
     1932                                        $page_template = $content_struct['wp_page_template'];
     1933                        } elseif ( $content_struct['post_type'] == 'post' ) {
     1934                                // This is the default, no changes needed
     1935                        } else {
     1936                                // No other post_type values are allowed here
     1937                                return new IXR_Error( 401, __( 'Invalid post type.' ) );
     1938                        }
     1939                }
     1940
     1941                if ( !current_user_can( $cap ) )
     1942                        return new IXR_Error( 401, $error_message );
     1943
     1944                // Let WordPress generate the post_name (slug) unless
     1945                // one has been provided.
     1946                $post_name = "";
     1947                if ( isset($content_struct["wp_slug"]) )
     1948                        $post_name = $content_struct["wp_slug"];
     1949
     1950                // Only use a password if one was given.
     1951                if ( isset($content_struct["wp_password"]) )
     1952                        $post_password = $content_struct["wp_password"];
     1953
     1954                // Only set a post parent if one was provided.
     1955                if ( isset($content_struct["wp_page_parent_id"]) )
     1956                        $post_parent = $content_struct["wp_page_parent_id"];
     1957
     1958                // Only set the menu_order if it was provided.
     1959                if ( isset($content_struct["wp_page_order"]) )
     1960                        $menu_order = $content_struct["wp_page_order"];
     1961
     1962                $post_author = $user->ID;
     1963
     1964                // If an author id was provided then use it instead.
     1965                if ( isset($content_struct["wp_author_id"]) && ($user->ID != $content_struct["wp_author_id"]) ) {
     1966                        switch ( $post_type ) {
     1967                                case "post":
     1968                                        if ( !current_user_can("edit_others_posts") )
     1969                                                return(new IXR_Error(401, __("You are not allowed to post as this user")));
     1970                                        break;
     1971                                case "page":
     1972                                        if ( !current_user_can("edit_others_pages") )
     1973                                                return(new IXR_Error(401, __("You are not allowed to create pages as this user")));
     1974                                        break;
     1975                                default:
     1976                                        return(new IXR_Error(401, __("Invalid post type.")));
     1977                                        break;
     1978                        }
     1979                        $post_author = $content_struct["wp_author_id"];
     1980                }
     1981
     1982                $post_title = $content_struct['title'];
     1983                $post_content = $content_struct['description'];
     1984
     1985                $post_status = $publish ? 'publish' : 'draft';
     1986
     1987                if ( isset( $content_struct["{$post_type}_status"] ) ) {
     1988                        switch ( $content_struct["{$post_type}_status"] ) {
     1989                                case 'draft':
     1990                                case 'private':
     1991                                case 'publish':
     1992                                        $post_status = $content_struct["{$post_type}_status"];
     1993                                        break;
     1994                                case 'pending':
     1995                                        // Pending is only valid for posts, not pages.
     1996                                        if ( $post_type === 'post' )
     1997                                                $post_status = $content_struct["{$post_type}_status"];
     1998                                        break;
     1999                                default:
     2000                                        $post_status = $publish ? 'publish' : 'draft';
     2001                                        break;
     2002                        }
     2003                }
     2004
     2005                $post_excerpt = $content_struct['mt_excerpt'];
     2006                $post_more = $content_struct['mt_text_more'];
     2007
     2008                $tags_input = $content_struct['mt_keywords'];
     2009
     2010                if ( isset($content_struct["mt_allow_comments"]) ) {
     2011                        if ( !is_numeric($content_struct["mt_allow_comments"]) ) {
     2012                                switch ( $content_struct["mt_allow_comments"] ) {
     2013                                        case "closed":
     2014                                                $comment_status = "closed";
     2015                                                break;
     2016                                        case "open":
     2017                                                $comment_status = "open";
     2018                                                break;
     2019                                        default:
     2020                                                $comment_status = get_option("default_comment_status");
     2021                                                break;
     2022                                }
     2023                        } else {
     2024                                switch ( (int) $content_struct["mt_allow_comments"] ) {
     2025                                        case 0:
     2026                                        case 2:
     2027                                                $comment_status = "closed";
     2028                                                break;
     2029                                        case 1:
     2030                                                $comment_status = "open";
     2031                                                break;
     2032                                        default:
     2033                                                $comment_status = get_option("default_comment_status");
     2034                                                break;
     2035                                }
     2036                        }
     2037                } else {
     2038                        $comment_status = get_option("default_comment_status");
     2039                }
     2040
     2041                if ( isset($content_struct["mt_allow_pings"]) ) {
     2042                        if ( !is_numeric($content_struct["mt_allow_pings"]) ) {
     2043                                switch ( $content_struct['mt_allow_pings'] ) {
     2044                                        case "closed":
     2045                                                $ping_status = "closed";
     2046                                                break;
     2047                                        case "open":
     2048                                                $ping_status = "open";
     2049                                                break;
     2050                                        default:
     2051                                                $ping_status = get_option("default_ping_status");
     2052                                                break;
     2053                                }
     2054                        } else {
     2055                                switch ( (int) $content_struct["mt_allow_pings"] ) {
     2056                                        case 0:
     2057                                                $ping_status = "closed";
     2058                                                break;
     2059                                        case 1:
     2060                                                $ping_status = "open";
     2061                                                break;
     2062                                        default:
     2063                                                $ping_status = get_option("default_ping_status");
     2064                                                break;
     2065                                }
     2066                        }
     2067                } else {
     2068                        $ping_status = get_option("default_ping_status");
     2069                }
     2070
     2071                if ( $post_more )
     2072                        $post_content = $post_content . "<!--more-->" . $post_more;
     2073
     2074                $to_ping = $content_struct['mt_tb_ping_urls'];
     2075                if ( is_array($to_ping) )
     2076                        $to_ping = implode(' ', $to_ping);
     2077
     2078                // Do some timestamp voodoo
     2079                if ( !empty( $content_struct['date_created_gmt'] ) )
     2080                        $dateCreated = str_replace( 'Z', '', $content_struct['date_created_gmt']->getIso() ) . 'Z'; // We know this is supposed to be GMT, so we're going to slap that Z on there by force
     2081                elseif ( !empty( $content_struct['dateCreated']) )
     2082                        $dateCreated = $content_struct['dateCreated']->getIso();
     2083
     2084                if ( !empty( $dateCreated ) ) {
     2085                        $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
     2086                        $post_date_gmt = iso8601_to_datetime($dateCreated, GMT);
     2087                } else {
     2088                        $post_date = current_time('mysql');
     2089                        $post_date_gmt = current_time('mysql', 1);
     2090                }
     2091
     2092                $catnames = $content_struct['categories'];
     2093                logIO('O', 'Post cats: ' . var_export($catnames,true));
     2094                $post_category = array();
     2095
     2096                if ( is_array($catnames) ) {
     2097                        foreach ($catnames as $cat) {
     2098                                $post_category[] = get_cat_ID($cat);
     2099                        }
     2100                }
     2101
     2102                // We've got all the data -- post it:
     2103                $postdata = compact('post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'to_ping', 'post_type', 'post_name', 'post_password', 'post_parent', 'menu_order', 'tags_input', 'page_template');
     2104
     2105                $post_ID = wp_insert_post($postdata, true);
     2106                if ( is_wp_error( $post_ID ) )
     2107                        return new IXR_Error(500, $post_ID->get_error_message());
     2108
     2109                if ( !$post_ID )
     2110                        return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.'));
     2111
     2112                // Only posts can be sticky
     2113                if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) {
     2114                        if ( $content_struct['sticky'] == true )
     2115                                stick_post( $post_ID );
     2116                        elseif ( $content_struct['sticky'] == false )
     2117                                unstick_post( $post_ID );
     2118                }
     2119
     2120                if ( isset($content_struct['custom_fields']) )
     2121                        $this->set_custom_fields($post_ID, $content_struct['custom_fields']);
     2122
     2123                // Handle enclosures
     2124                $this->add_enclosure_if_new($post_ID, $content_struct['enclosure']);
     2125
     2126                $this->attach_uploads( $post_ID, $post_content );
     2127
     2128                logIO('O', "Posted ! ID: $post_ID");
     2129
     2130                return strval($post_ID);
     2131        }
     2132
     2133        function add_enclosure_if_new($post_ID, $enclosure) {
     2134                if ( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) {
     2135
     2136                        $encstring = $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type'];
     2137                        $found = false;
     2138                        foreach ( (array) get_post_custom($post_ID) as $key => $val) {
     2139                                if ($key == 'enclosure') {
     2140                                        foreach ( (array) $val as $enc ) {
     2141                                                if ($enc == $encstring) {
     2142                                                        $found = true;
     2143                                                        break 2;
     2144                                                }
     2145                                        }
     2146                                }
     2147                        }
     2148                        if (!$found)
     2149                                add_post_meta( $post_ID, 'enclosure', $encstring );
     2150                }
     2151        }
     2152
     2153        /**
     2154         * Attach upload to a post.
     2155         *
     2156         * @since 2.1.0
     2157         *
     2158         * @param int $post_ID Post ID.
     2159         * @param string $post_content Post Content for attachment.
     2160         */
     2161        function attach_uploads( $post_ID, $post_content ) {
     2162                global $wpdb;
     2163
     2164                // find any unattached files
     2165                $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '0' AND post_type = 'attachment'" );
     2166                if ( is_array( $attachments ) ) {
     2167                        foreach ( $attachments as $file ) {
     2168                                if ( strpos( $post_content, $file->guid ) !== false )
     2169                                        $wpdb->update($wpdb->posts, array('post_parent' => $post_ID), array('ID' => $file->ID) );
     2170                        }
     2171                }
     2172        }
     2173
     2174        /**
     2175         * Edit a post.
     2176         *
     2177         * @since 1.5.0
     2178         *
     2179         * @param array $args Method parameters.
     2180         * @return bool True on success.
     2181         */
     2182        function mw_editPost($args) {
     2183
     2184                $this->escape($args);
     2185
     2186                $post_ID     = (int) $args[0];
     2187                $username  = $args[1];
     2188                $password   = $args[2];
     2189                $content_struct = $args[3];
     2190                $publish     = $args[4];
     2191
     2192                if ( !$user = $this->login($username, $password) )
     2193                        return $this->error;
     2194
     2195                do_action('xmlrpc_call', 'metaWeblog.editPost');
     2196
     2197                $cap = ( $publish ) ? 'publish_posts' : 'edit_posts';
     2198                $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
     2199                $post_type = 'post';
     2200                $page_template = '';
     2201                if ( !empty( $content_struct['post_type'] ) ) {
     2202                        if ( $content_struct['post_type'] == 'page' ) {
     2203                                $cap = ( $publish ) ? 'publish_pages' : 'edit_pages';
     2204                                $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' );
     2205                                $post_type = 'page';
     2206                                if ( !empty( $content_struct['wp_page_template'] ) )
     2207                                        $page_template = $content_struct['wp_page_template'];
     2208                        } elseif ( $content_struct['post_type'] == 'post' ) {
     2209                                // This is the default, no changes needed
     2210                        } else {
     2211                                // No other post_type values are allowed here
     2212                                return new IXR_Error( 401, __( 'Invalid post type.' ) );
     2213                        }
     2214                }
     2215
     2216                if ( !current_user_can( $cap ) )
     2217                        return new IXR_Error( 401, $error_message );
     2218
     2219                $postdata = wp_get_single_post($post_ID, ARRAY_A);
     2220
     2221                // If there is no post data for the give post id, stop
     2222                // now and return an error.  Other wise a new post will be
     2223                // created (which was the old behavior).
     2224                if ( empty($postdata["ID"]) )
     2225                        return(new IXR_Error(404, __("Invalid post ID.")));
     2226
     2227                $this->escape($postdata);
     2228                extract($postdata, EXTR_SKIP);
     2229
     2230                // Let WordPress manage slug if none was provided.
     2231                $post_name = "";
     2232                if ( isset($content_struct["wp_slug"]) )
     2233                        $post_name = $content_struct["wp_slug"];
     2234
     2235                // Only use a password if one was given.
     2236                if ( isset($content_struct["wp_password"]) )
     2237                        $post_password = $content_struct["wp_password"];
     2238
     2239                // Only set a post parent if one was given.
     2240                if ( isset($content_struct["wp_page_parent_id"]) )
     2241                        $post_parent = $content_struct["wp_page_parent_id"];
     2242
     2243                // Only set the menu_order if it was given.
     2244                if ( isset($content_struct["wp_page_order"]) )
     2245                        $menu_order = $content_struct["wp_page_order"];
     2246
     2247                $post_author = $postdata["post_author"];
     2248
     2249                // Only set the post_author if one is set.
     2250                if ( isset($content_struct["wp_author_id"]) && ($user->ID != $content_struct["wp_author_id"]) ) {
     2251                        switch ( $post_type ) {
     2252                                case "post":
     2253                                        if ( !current_user_can("edit_others_posts") )
     2254                                                return(new IXR_Error(401, __("You are not allowed to change the post author as this user.")));
     2255                                        break;
     2256                                case "page":
     2257                                        if ( !current_user_can("edit_others_pages") )
     2258                                                return(new IXR_Error(401, __("You are not allowed to change the page author as this user.")));
     2259                                        break;
     2260                                default:
     2261                                        return(new IXR_Error(401, __("Invalid post type.")));
     2262                                        break;
     2263                        }
     2264                        $post_author = $content_struct["wp_author_id"];
     2265                }
     2266
     2267                if ( isset($content_struct["mt_allow_comments"]) ) {
     2268                        if ( !is_numeric($content_struct["mt_allow_comments"]) ) {
     2269                                switch ( $content_struct["mt_allow_comments"] ) {
     2270                                        case "closed":
     2271                                                $comment_status = "closed";
     2272                                                break;
     2273                                        case "open":
     2274                                                $comment_status = "open";
     2275                                                break;
     2276                                        default:
     2277                                                $comment_status = get_option("default_comment_status");
     2278                                                break;
     2279                                }
     2280                        } else {
     2281                                switch ( (int) $content_struct["mt_allow_comments"] ) {
     2282                                        case 0:
     2283                                        case 2:
     2284                                                $comment_status = "closed";
     2285                                                break;
     2286                                        case 1:
     2287                                                $comment_status = "open";
     2288                                                break;
     2289                                        default:
     2290                                                $comment_status = get_option("default_comment_status");
     2291                                                break;
     2292                                }
     2293                        }
     2294                }
     2295
     2296                if ( isset($content_struct["mt_allow_pings"]) ) {
     2297                        if ( !is_numeric($content_struct["mt_allow_pings"]) ) {
     2298                                switch ( $content_struct["mt_allow_pings"] ) {
     2299                                        case "closed":
     2300                                                $ping_status = "closed";
     2301                                                break;
     2302                                        case "open":
     2303                                                $ping_status = "open";
     2304                                                break;
     2305                                        default:
     2306                                                $ping_status = get_option("default_ping_status");
     2307                                                break;
     2308                                }
     2309                        } else {
     2310                                switch ( (int) $content_struct["mt_allow_pings"] ) {
     2311                                        case 0:
     2312                                                $ping_status = "closed";
     2313                                                break;
     2314                                        case 1:
     2315                                                $ping_status = "open";
     2316                                                break;
     2317                                        default:
     2318                                                $ping_status = get_option("default_ping_status");
     2319                                                break;
     2320                                }
     2321                        }
     2322                }
     2323
     2324                $post_title = $content_struct['title'];
     2325                $post_content = $content_struct['description'];
     2326                $catnames = $content_struct['categories'];
     2327
     2328                $post_category = array();
     2329
     2330                if ( is_array($catnames) ) {
     2331                        foreach ($catnames as $cat) {
     2332                                $post_category[] = get_cat_ID($cat);
     2333                        }
     2334                }
     2335
     2336                $post_excerpt = $content_struct['mt_excerpt'];
     2337                $post_more = $content_struct['mt_text_more'];
     2338
     2339                $post_status = $publish ? 'publish' : 'draft';
     2340                if ( isset( $content_struct["{$post_type}_status"] ) ) {
     2341                        switch( $content_struct["{$post_type}_status"] ) {
     2342                                case 'draft':
     2343                                case 'private':
     2344                                case 'publish':
     2345                                        $post_status = $content_struct["{$post_type}_status"];
     2346                                        break;
     2347                                case 'pending':
     2348                                        // Pending is only valid for posts, not pages.
     2349                                        if ( $post_type === 'post' )
     2350                                                $post_status = $content_struct["{$post_type}_status"];
     2351                                        break;
     2352                                default:
     2353                                        $post_status = $publish ? 'publish' : 'draft';
     2354                                        break;
     2355                        }
     2356                }
     2357
     2358                $tags_input = $content_struct['mt_keywords'];
     2359
     2360                if ( ('publish' == $post_status) ) {
     2361                        if ( ( 'page' == $post_type ) && !current_user_can('publish_pages') )
     2362                                return new IXR_Error(401, __('Sorry, you do not have the right to publish this page.'));
     2363                        else if ( !current_user_can('publish_posts') )
     2364                                return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.'));
     2365                }
     2366
     2367                if ( $post_more )
     2368                        $post_content = $post_content . "<!--more-->" . $post_more;
     2369
     2370                $to_ping = $content_struct['mt_tb_ping_urls'];
     2371                if ( is_array($to_ping) )
     2372                        $to_ping = implode(' ', $to_ping);
     2373
     2374                // Do some timestamp voodoo
     2375                if ( !empty( $content_struct['date_created_gmt'] ) )
     2376                        $dateCreated = str_replace( 'Z', '', $content_struct['date_created_gmt']->getIso() ) . 'Z'; // We know this is supposed to be GMT, so we're going to slap that Z on there by force
     2377                elseif ( !empty( $content_struct['dateCreated']) )
     2378                        $dateCreated = $content_struct['dateCreated']->getIso();
     2379
     2380                if ( !empty( $dateCreated ) ) {
     2381                        $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
     2382                        $post_date_gmt = iso8601_to_datetime($dateCreated, GMT);
     2383                } else {
     2384                        $post_date     = $postdata['post_date'];
     2385                        $post_date_gmt = $postdata['post_date_gmt'];
     2386                }
     2387
     2388                // We've got all the data -- post it:
     2389                $newpost = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'post_date', 'post_date_gmt', 'to_ping', 'post_name', 'post_password', 'post_parent', 'menu_order', 'post_author', 'tags_input', 'page_template');
     2390
     2391                $result = wp_update_post($newpost, true);
     2392                if ( is_wp_error( $result ) )
     2393                        return new IXR_Error(500, $result->get_error_message());
     2394
     2395                if ( !$result )
     2396                        return new IXR_Error(500, __('Sorry, your entry could not be edited. Something wrong happened.'));
     2397
     2398                // Only posts can be sticky
     2399                if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) {
     2400                        if ( $content_struct['sticky'] == true )
     2401                                stick_post( $post_ID );
     2402                        elseif ( $content_struct['sticky'] == false )
     2403                                unstick_post( $post_ID );
     2404                }
     2405
     2406                if ( isset($content_struct['custom_fields']) )
     2407                        $this->set_custom_fields($post_ID, $content_struct['custom_fields']);
     2408
     2409                // Handle enclosures
     2410                $this->add_enclosure_if_new($post_ID, $content_struct['enclosure']);
     2411
     2412                $this->attach_uploads( $ID, $post_content );
     2413
     2414                logIO('O',"(MW) Edited ! ID: $post_ID");
     2415
     2416                return true;
     2417        }
     2418
     2419        /**
     2420         * Retrieve post.
     2421         *
     2422         * @since 1.5.0
     2423         *
     2424         * @param array $args Method parameters.
     2425         * @return array
     2426         */
     2427        function mw_getPost($args) {
     2428
     2429                $this->escape($args);
     2430
     2431                $post_ID     = (int) $args[0];
     2432                $username  = $args[1];
     2433                $password   = $args[2];
     2434
     2435                if ( !$user = $this->login($username, $password) )
     2436                        return $this->error;
     2437
     2438                if ( !current_user_can( 'edit_post', $post_ID ) )
     2439                        return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
     2440
     2441                do_action('xmlrpc_call', 'metaWeblog.getPost');
     2442
     2443                $postdata = wp_get_single_post($post_ID, ARRAY_A);
     2444
     2445                if ($postdata['post_date'] != '') {
     2446                        $post_date = mysql2date('Ymd\TH:i:s', $postdata['post_date'], false);
     2447                        $post_date_gmt = mysql2date('Ymd\TH:i:s', $postdata['post_date_gmt'], false);
     2448
     2449                        // For drafts use the GMT version of the post date
     2450                        if ( $postdata['post_status'] == 'draft' )
     2451                                $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $postdata['post_date'] ), 'Ymd\TH:i:s' );
     2452
     2453                        $categories = array();
     2454                        $catids = wp_get_post_categories($post_ID);
     2455                        foreach($catids as $catid)
     2456                                $categories[] = get_cat_name($catid);
     2457
     2458                        $tagnames = array();
     2459                        $tags = wp_get_post_tags( $post_ID );
     2460                        if ( !empty( $tags ) ) {
     2461                                foreach ( $tags as $tag )
     2462                                        $tagnames[] = $tag->name;
     2463                                $tagnames = implode( ', ', $tagnames );
     2464                        } else {
     2465                                $tagnames = '';
     2466                        }
     2467
     2468                        $post = get_extended($postdata['post_content']);
     2469                        $link = post_permalink($postdata['ID']);
     2470
     2471                        // Get the author info.
     2472                        $author = get_userdata($postdata['post_author']);
     2473
     2474                        $allow_comments = ('open' == $postdata['comment_status']) ? 1 : 0;
     2475                        $allow_pings = ('open' == $postdata['ping_status']) ? 1 : 0;
     2476
     2477                        // Consider future posts as published
     2478                        if ( $postdata['post_status'] === 'future' )
     2479                                $postdata['post_status'] = 'publish';
     2480
     2481                        $sticky = false;
     2482                        if ( is_sticky( $post_ID ) )
     2483                                $sticky = true;
     2484
     2485                        $enclosure = array();
     2486                        foreach ( (array) get_post_custom($post_ID) as $key => $val) {
     2487                                if ($key == 'enclosure') {
     2488                                        foreach ( (array) $val as $enc ) {
     2489                                                $encdata = split("\n", $enc);
     2490                                                $enclosure['url'] = trim(htmlspecialchars($encdata[0]));
     2491                                                $enclosure['length'] = (int) trim($encdata[1]);
     2492                                                $enclosure['type'] = trim($encdata[2]);
     2493                                                break 2;
     2494                                        }
     2495                                }
     2496                        }
     2497
     2498                        $resp = array(
     2499                                'dateCreated' => new IXR_Date($post_date),
     2500                                'userid' => $postdata['post_author'],
     2501                                'postid' => $postdata['ID'],
     2502                                'description' => $post['main'],
     2503                                'title' => $postdata['post_title'],
     2504                                'link' => $link,
     2505                                'permaLink' => $link,
     2506                                // commented out because no other tool seems to use this
     2507                                //            'content' => $entry['post_content'],
     2508                                'categories' => $categories,
     2509                                'mt_excerpt' => $postdata['post_excerpt'],
     2510                                'mt_text_more' => $post['extended'],
     2511                                'mt_allow_comments' => $allow_comments,
     2512                                'mt_allow_pings' => $allow_pings,
     2513                                'mt_keywords' => $tagnames,
     2514                                'wp_slug' => $postdata['post_name'],
     2515                                'wp_password' => $postdata['post_password'],
     2516                                'wp_author_id' => $author->ID,
     2517                                'wp_author_display_name'        => $author->display_name,
     2518                                'date_created_gmt' => new IXR_Date($post_date_gmt),
     2519                                'post_status' => $postdata['post_status'],
     2520                                'custom_fields' => $this->get_custom_fields($post_ID),
     2521                                'sticky' => $sticky
     2522                        );
     2523
     2524                        if ( !empty($enclosure) ) $resp['enclosure'] = $enclosure;
     2525
     2526                        return $resp;
     2527                } else {
     2528                        return new IXR_Error(404, __('Sorry, no such post.'));
     2529                }
     2530        }
     2531
     2532        /**
     2533         * Retrieve list of recent posts.
     2534         *
     2535         * @since 1.5.0
     2536         *
     2537         * @param array $args Method parameters.
     2538         * @return array
     2539         */
     2540        function mw_getRecentPosts($args) {
     2541
     2542                $this->escape($args);
     2543
     2544                $blog_ID     = (int) $args[0];
     2545                $username  = $args[1];
     2546                $password   = $args[2];
     2547                $num_posts   = (int) $args[3];
     2548
     2549                if ( !$user = $this->login($username, $password) )
     2550                        return $this->error;
     2551
     2552                do_action('xmlrpc_call', 'metaWeblog.getRecentPosts');
     2553
     2554                $posts_list = wp_get_recent_posts($num_posts);
     2555
     2556                if ( !$posts_list )
     2557                        return array( );
     2558
     2559                foreach ($posts_list as $entry) {
     2560                        if ( !current_user_can( 'edit_post', $entry['ID'] ) )
     2561                                continue;
     2562
     2563                        $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false);
     2564                        $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt'], false);
     2565
     2566                        // For drafts use the GMT version of the date
     2567                        if ( $entry['post_status'] == 'draft' )
     2568                                $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $entry['post_date'] ), 'Ymd\TH:i:s' );
     2569
     2570                        $categories = array();
     2571                        $catids = wp_get_post_categories($entry['ID']);
     2572                        foreach( $catids as $catid )
     2573                                $categories[] = get_cat_name($catid);
     2574
     2575                        $tagnames = array();
     2576                        $tags = wp_get_post_tags( $entry['ID'] );
     2577                        if ( !empty( $tags ) ) {
     2578                                foreach ( $tags as $tag ) {
     2579                                        $tagnames[] = $tag->name;
     2580                                }
     2581                                $tagnames = implode( ', ', $tagnames );
     2582                        } else {
     2583                                $tagnames = '';
     2584                        }
     2585
     2586                        $post = get_extended($entry['post_content']);
     2587                        $link = post_permalink($entry['ID']);
     2588
     2589                        // Get the post author info.
     2590                        $author = get_userdata($entry['post_author']);
     2591
     2592                        $allow_comments = ('open' == $entry['comment_status']) ? 1 : 0;
     2593                        $allow_pings = ('open' == $entry['ping_status']) ? 1 : 0;
     2594
     2595                        // Consider future posts as published
     2596                        if ( $entry['post_status'] === 'future' )
     2597                                $entry['post_status'] = 'publish';
     2598
     2599                        $struct[] = array(
     2600                                'dateCreated' => new IXR_Date($post_date),
     2601                                'userid' => $entry['post_author'],
     2602                                'postid' => $entry['ID'],
     2603                                'description' => $post['main'],
     2604                                'title' => $entry['post_title'],
     2605                                'link' => $link,
     2606                                'permaLink' => $link,
     2607                                // commented out because no other tool seems to use this
     2608                                // 'content' => $entry['post_content'],
     2609                                'categories' => $categories,
     2610                                'mt_excerpt' => $entry['post_excerpt'],
     2611                                'mt_text_more' => $post['extended'],
     2612                                'mt_allow_comments' => $allow_comments,
     2613                                'mt_allow_pings' => $allow_pings,
     2614                                'mt_keywords' => $tagnames,
     2615                                'wp_slug' => $entry['post_name'],
     2616                                'wp_password' => $entry['post_password'],
     2617                                'wp_author_id' => $author->ID,
     2618                                'wp_author_display_name' => $author->display_name,
     2619                                'date_created_gmt' => new IXR_Date($post_date_gmt),
     2620                                'post_status' => $entry['post_status'],
     2621                                'custom_fields' => $this->get_custom_fields($entry['ID'])
     2622                        );
     2623
     2624                }
     2625
     2626                $recent_posts = array();
     2627                for ( $j=0; $j<count($struct); $j++ ) {
     2628                        array_push($recent_posts, $struct[$j]);
     2629                }
     2630
     2631                return $recent_posts;
     2632        }
     2633
     2634        /**
     2635         * Retrieve the list of categories on a given blog.
     2636         *
     2637         * @since 1.5.0
     2638         *
     2639         * @param array $args Method parameters.
     2640         * @return array
     2641         */
     2642        function mw_getCategories($args) {
     2643
     2644                $this->escape($args);
     2645
     2646                $blog_ID     = (int) $args[0];
     2647                $username  = $args[1];
     2648                $password   = $args[2];
     2649
     2650                if ( !$user = $this->login($username, $password) )
     2651                        return $this->error;
     2652
     2653                if ( !current_user_can( 'edit_posts' ) )
     2654                        return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );
     2655
     2656                do_action('xmlrpc_call', 'metaWeblog.getCategories');
     2657
     2658                $categories_struct = array();
     2659
     2660                if ( $cats = get_categories(array('get' => 'all')) ) {
     2661                        foreach ( $cats as $cat ) {
     2662                                $struct['categoryId'] = $cat->term_id;
     2663                                $struct['parentId'] = $cat->parent;
     2664                                $struct['description'] = $cat->name;
     2665                                $struct['categoryDescription'] = $cat->description;
     2666                                $struct['categoryName'] = $cat->name;
     2667                                $struct['htmlUrl'] = esc_html(get_category_link($cat->term_id));
     2668                                $struct['rssUrl'] = esc_html(get_category_feed_link($cat->term_id, 'rss2'));
     2669
     2670                                $categories_struct[] = $struct;
     2671                        }
     2672                }
     2673
     2674                return $categories_struct;
     2675        }
     2676
     2677        /**
     2678         * Uploads a file, following your settings.
     2679         *
     2680         * Adapted from a patch by Johann Richard.
     2681         *
     2682         * @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/
     2683         *
     2684         * @since 1.5.0
     2685         *
     2686         * @param array $args Method parameters.
     2687         * @return array
     2688         */
     2689        function mw_newMediaObject($args) {
     2690                global $wpdb;
     2691
     2692                $blog_ID     = (int) $args[0];
     2693                $username  = $wpdb->escape($args[1]);
     2694                $password   = $wpdb->escape($args[2]);
     2695                $data        = $args[3];
     2696
     2697                $name = sanitize_file_name( $data['name'] );
     2698                $type = $data['type'];
     2699                $bits = $data['bits'];
     2700
     2701                logIO('O', '(MW) Received '.strlen($bits).' bytes');
     2702
     2703                if ( !$user = $this->login($username, $password) )
     2704                        return $this->error;
     2705
     2706                do_action('xmlrpc_call', 'metaWeblog.newMediaObject');
     2707
     2708                if ( !current_user_can('upload_files') ) {
     2709                        logIO('O', '(MW) User does not have upload_files capability');
     2710                        $this->error = new IXR_Error(401, __('You are not allowed to upload files to this site.'));
     2711                        return $this->error;
     2712                }
     2713
     2714                if ( $upload_err = apply_filters( "pre_upload_error", false ) )
     2715                        return new IXR_Error(500, $upload_err);
     2716
     2717                if ( !empty($data["overwrite"]) && ($data["overwrite"] == true) ) {
     2718                        // Get postmeta info on the object.
     2719                        $old_file = $wpdb->get_row("
     2720                                SELECT ID
     2721                                FROM {$wpdb->posts}
     2722                                WHERE post_title = '{$name}'
     2723                                        AND post_type = 'attachment'
     2724                        ");
     2725
     2726                        // Delete previous file.
     2727                        wp_delete_attachment($old_file->ID);
     2728
     2729                        // Make sure the new name is different by pre-pending the
     2730                        // previous post id.
     2731                        $filename = preg_replace("/^wpid\d+-/", "", $name);
     2732                        $name = "wpid{$old_file->ID}-{$filename}";
     2733                }
     2734
     2735                $upload = wp_upload_bits($name, $type, $bits);
     2736                if ( ! empty($upload['error']) ) {
     2737                        $errorString = sprintf(__('Could not write file %1$s (%2$s)'), $name, $upload['error']);
     2738                        logIO('O', '(MW) ' . $errorString);
     2739                        return new IXR_Error(500, $errorString);
     2740                }
     2741                // Construct the attachment array
     2742                // attach to post_id 0
     2743                $post_id = 0;
     2744                $attachment = array(
     2745                        'post_title' => $name,
     2746                        'post_content' => '',
     2747                        'post_type' => 'attachment',
     2748                        'post_parent' => $post_id,
     2749                        'post_mime_type' => $type,
     2750                        'guid' => $upload[ 'url' ]
     2751                );
     2752
     2753                // Save the data
     2754                $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id );
     2755                wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );
     2756
     2757                return apply_filters( 'wp_handle_upload', array( 'file' => $name, 'url' => $upload[ 'url' ], 'type' => $type ), 'upload' );
     2758        }
     2759
     2760        /* MovableType API functions
     2761         * specs on http://www.movabletype.org/docs/mtmanual_programmatic.html
     2762         */
     2763
     2764        /**
     2765         * Retrieve the post titles of recent posts.
     2766         *
     2767         * @since 1.5.0
     2768         *
     2769         * @param array $args Method parameters.
     2770         * @return array
     2771         */
     2772        function mt_getRecentPostTitles($args) {
     2773
     2774                $this->escape($args);
     2775
     2776                $blog_ID     = (int) $args[0];
     2777                $username  = $args[1];
     2778                $password   = $args[2];
     2779                $num_posts   = (int) $args[3];
     2780
     2781                if ( !$user = $this->login($username, $password) )
     2782                        return $this->error;
     2783
     2784                do_action('xmlrpc_call', 'mt.getRecentPostTitles');
     2785
     2786                $posts_list = wp_get_recent_posts($num_posts);
     2787
     2788                if ( !$posts_list ) {
     2789                        $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
     2790                        return $this->error;
     2791                }
     2792
     2793                foreach ($posts_list as $entry) {
     2794                        if ( !current_user_can( 'edit_post', $entry['ID'] ) )
     2795                                continue;
     2796
     2797                        $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false);
     2798                        $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt'], false);
     2799
     2800                        // For drafts use the GMT version of the date
     2801                        if ( $entry['post_status'] == 'draft' )
     2802                                $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $entry['post_date'] ), 'Ymd\TH:i:s' );
     2803
     2804                        $struct[] = array(
     2805                                'dateCreated' => new IXR_Date($post_date),
     2806                                'userid' => $entry['post_author'],
     2807                                'postid' => $entry['ID'],
     2808                                'title' => $entry['post_title'],
     2809                                'date_created_gmt' => new IXR_Date($post_date_gmt)
     2810                        );
     2811
     2812                }
     2813
     2814                $recent_posts = array();
     2815                for ( $j=0; $j<count($struct); $j++ ) {
     2816                        array_push($recent_posts, $struct[$j]);
     2817                }
     2818
     2819                return $recent_posts;
     2820        }
     2821
     2822        /**
     2823         * Retrieve list of all categories on blog.
     2824         *
     2825         * @since 1.5.0
     2826         *
     2827         * @param array $args Method parameters.
     2828         * @return array
     2829         */
     2830        function mt_getCategoryList($args) {
     2831
     2832                $this->escape($args);
     2833
     2834                $blog_ID     = (int) $args[0];
     2835                $username  = $args[1];
     2836                $password   = $args[2];
     2837
     2838                if ( !$user = $this->login($username, $password) )
     2839                        return $this->error;
     2840
     2841                if ( !current_user_can( 'edit_posts' ) )
     2842                        return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );
     2843
     2844                do_action('xmlrpc_call', 'mt.getCategoryList');
     2845
     2846                $categories_struct = array();
     2847
     2848                if ( $cats = get_categories(array('hide_empty' => 0, 'hierarchical' => 0)) ) {
     2849                        foreach ( $cats as $cat ) {
     2850                                $struct['categoryId'] = $cat->term_id;
     2851                                $struct['categoryName'] = $cat->name;
     2852
     2853                                $categories_struct[] = $struct;
     2854                        }
     2855                }
     2856
     2857                return $categories_struct;
     2858        }
     2859
     2860        /**
     2861         * Retrieve post categories.
     2862         *
     2863         * @since 1.5.0
     2864         *
     2865         * @param array $args Method parameters.
     2866         * @return array
     2867         */
     2868        function mt_getPostCategories($args) {
     2869
     2870                $this->escape($args);
     2871
     2872                $post_ID     = (int) $args[0];
     2873                $username  = $args[1];
     2874                $password   = $args[2];
     2875
     2876                if ( !$user = $this->login($username, $password) )
     2877                        return $this->error;
     2878
     2879                if ( !current_user_can( 'edit_post', $post_ID ) )
     2880                        return new IXR_Error( 401, __( 'Sorry, you can not edit this post.' ) );
     2881
     2882                do_action('xmlrpc_call', 'mt.getPostCategories');
     2883
     2884                $categories = array();
     2885                $catids = wp_get_post_categories(intval($post_ID));
     2886                // first listed category will be the primary category
     2887                $isPrimary = true;
     2888                foreach ( $catids as $catid ) {
     2889                        $categories[] = array(
     2890                                'categoryName' => get_cat_name($catid),
     2891                                'categoryId' => (string) $catid,
     2892                                'isPrimary' => $isPrimary
     2893                        );
     2894                        $isPrimary = false;
     2895                }
     2896
     2897                return $categories;
     2898        }
     2899
     2900        /**
     2901         * Sets categories for a post.
     2902         *
     2903         * @since 1.5.0
     2904         *
     2905         * @param array $args Method parameters.
     2906         * @return bool True on success.
     2907         */
     2908        function mt_setPostCategories($args) {
     2909
     2910                $this->escape($args);
     2911
     2912                $post_ID     = (int) $args[0];
     2913                $username  = $args[1];
     2914                $password   = $args[2];
     2915                $categories  = $args[3];
     2916
     2917                if ( !$user = $this->login($username, $password) )
     2918                        return $this->error;
     2919
     2920                do_action('xmlrpc_call', 'mt.setPostCategories');
     2921
     2922                if ( !current_user_can('edit_post', $post_ID) )
     2923                        return new IXR_Error(401, __('Sorry, you cannot edit this post.'));
     2924
     2925                foreach ( $categories as $cat ) {
     2926                        $catids[] = $cat['categoryId'];
     2927                }
     2928
     2929                wp_set_post_categories($post_ID, $catids);
     2930
     2931                return true;
     2932        }
     2933
     2934        /**
     2935         * Retrieve an array of methods supported by this server.
     2936         *
     2937         * @since 1.5.0
     2938         *
     2939         * @param array $args Method parameters.
     2940         * @return array
     2941         */
     2942        function mt_supportedMethods($args) {
     2943
     2944                do_action('xmlrpc_call', 'mt.supportedMethods');
     2945
     2946                $supported_methods = array();
     2947                foreach ( $this->methods as $key => $value ) {
     2948                        $supported_methods[] = $key;
     2949                }
     2950
     2951                return $supported_methods;
     2952        }
     2953
     2954        /**
     2955         * Retrieve an empty array because we don't support per-post text filters.
     2956         *
     2957         * @since 1.5.0
     2958         *
     2959         * @param array $args Method parameters.
     2960         */
     2961        function mt_supportedTextFilters($args) {
     2962                do_action('xmlrpc_call', 'mt.supportedTextFilters');
     2963                return apply_filters('xmlrpc_text_filters', array());
     2964        }
     2965
     2966        /**
     2967         * Retrieve trackbacks sent to a given post.
     2968         *
     2969         * @since 1.5.0
     2970         *
     2971         * @param array $args Method parameters.
     2972         * @return mixed
     2973         */
     2974        function mt_getTrackbackPings($args) {
     2975
     2976                global $wpdb;
     2977
     2978                $post_ID = intval($args);
     2979
     2980                do_action('xmlrpc_call', 'mt.getTrackbackPings');
     2981
     2982                $actual_post = wp_get_single_post($post_ID, ARRAY_A);
     2983
     2984                if ( !$actual_post )
     2985                        return new IXR_Error(404, __('Sorry, no such post.'));
     2986
     2987                $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_ID) );
     2988
     2989                if ( !$comments )
     2990                        return array();
     2991
     2992                $trackback_pings = array();
     2993                foreach ( $comments as $comment ) {
     2994                        if ( 'trackback' == $comment->comment_type ) {
     2995                                $content = $comment->comment_content;
     2996                                $title = substr($content, 8, (strpos($content, '</strong>') - 8));
     2997                                $trackback_pings[] = array(
     2998                                        'pingTitle' => $title,
     2999                                        'pingURL'   => $comment->comment_author_url,
     3000                                        'pingIP'    => $comment->comment_author_IP
     3001                                );
     3002                        }
     3003                }
     3004
     3005                return $trackback_pings;
     3006        }
     3007
     3008        /**
     3009         * Sets a post's publish status to 'publish'.
     3010         *
     3011         * @since 1.5.0
     3012         *
     3013         * @param array $args Method parameters.
     3014         * @return int
     3015         */
     3016        function mt_publishPost($args) {
     3017
     3018                $this->escape($args);
     3019
     3020                $post_ID     = (int) $args[0];
     3021                $username  = $args[1];
     3022                $password   = $args[2];
     3023
     3024                if ( !$user = $this->login($username, $password) )
     3025                        return $this->error;
     3026
     3027                do_action('xmlrpc_call', 'mt.publishPost');
     3028
     3029                if ( !current_user_can('edit_post', $post_ID) )
     3030                        return new IXR_Error(401, __('Sorry, you cannot edit this post.'));
     3031
     3032                $postdata = wp_get_single_post($post_ID,ARRAY_A);
     3033
     3034                $postdata['post_status'] = 'publish';
     3035
     3036                // retain old cats
     3037                $cats = wp_get_post_categories($post_ID);
     3038                $postdata['post_category'] = $cats;
     3039                $this->escape($postdata);
     3040
     3041                $result = wp_update_post($postdata);
     3042
     3043                return $result;
     3044        }
     3045
     3046        /* PingBack functions
     3047         * specs on www.hixie.ch/specs/pingback/pingback
     3048         */
     3049
     3050        /**
     3051         * Retrieves a pingback and registers it.
     3052         *
     3053         * @since 1.5.0
     3054         *
     3055         * @param array $args Method parameters.
     3056         * @return array
     3057         */
     3058        function pingback_ping($args) {
     3059                global $wpdb;
     3060
     3061                do_action('xmlrpc_call', 'pingback.ping');
     3062
     3063                $this->escape($args);
     3064
     3065                $pagelinkedfrom = $args[0];
     3066                $pagelinkedto   = $args[1];
     3067
     3068                $title = '';
     3069
     3070                $pagelinkedfrom = str_replace('&amp;', '&', $pagelinkedfrom);
     3071                $pagelinkedto = str_replace('&amp;', '&', $pagelinkedto);
     3072                $pagelinkedto = str_replace('&', '&amp;', $pagelinkedto);
     3073
     3074                // Check if the page linked to is in our site
     3075                $pos1 = strpos($pagelinkedto, str_replace(array('http://www.','http://','https://www.','https://'), '', get_option('home')));
     3076                if ( !$pos1 )
     3077                        return new IXR_Error(0, __('Is there no link to us?'));
     3078
     3079                // let's find which post is linked to
     3080                // FIXME: does url_to_postid() cover all these cases already?
     3081                //        if so, then let's use it and drop the old code.
     3082                $urltest = parse_url($pagelinkedto);
     3083                if ( $post_ID = url_to_postid($pagelinkedto) ) {
     3084                        $way = 'url_to_postid()';
     3085                } elseif ( preg_match('#p/[0-9]{1,}#', $urltest['path'], $match) ) {
     3086                        // the path defines the post_ID (archives/p/XXXX)
     3087                        $blah = explode('/', $match[0]);
     3088                        $post_ID = (int) $blah[1];
     3089                        $way = 'from the path';
     3090                } elseif ( preg_match('#p=[0-9]{1,}#', $urltest['query'], $match) ) {
     3091                        // the querystring defines the post_ID (?p=XXXX)
     3092                        $blah = explode('=', $match[0]);
     3093                        $post_ID = (int) $blah[1];
     3094                        $way = 'from the querystring';
     3095                } elseif ( isset($urltest['fragment']) ) {
     3096                        // an #anchor is there, it's either...
     3097                        if ( intval($urltest['fragment']) ) {
     3098                                // ...an integer #XXXX (simpliest case)
     3099                                $post_ID = (int) $urltest['fragment'];
     3100                                $way = 'from the fragment (numeric)';
     3101                        } elseif ( preg_match('/post-[0-9]+/',$urltest['fragment']) ) {
     3102                                // ...a post id in the form 'post-###'
     3103                                $post_ID = preg_replace('/[^0-9]+/', '', $urltest['fragment']);
     3104                                $way = 'from the fragment (post-###)';
     3105                        } elseif ( is_string($urltest['fragment']) ) {
     3106                                // ...or a string #title, a little more complicated
     3107                                $title = preg_replace('/[^a-z0-9]/i', '.', $urltest['fragment']);
     3108                                $sql = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", $title);
     3109                                if (! ($post_ID = $wpdb->get_var($sql)) ) {
     3110                                        // returning unknown error '0' is better than die()ing
     3111                                        return new IXR_Error(0, '');
     3112                                }
     3113                                $way = 'from the fragment (title)';
     3114                        }
     3115                } else {
     3116                        // TODO: Attempt to extract a post ID from the given URL
     3117                        return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.'));
     3118                }
     3119                $post_ID = (int) $post_ID;
     3120
     3121
     3122                logIO("O","(PB) URL='$pagelinkedto' ID='$post_ID' Found='$way'");
     3123
     3124                $post = get_post($post_ID);
     3125
     3126                if ( !$post ) // Post_ID not found
     3127                        return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.'));
     3128
     3129                if ( $post_ID == url_to_postid($pagelinkedfrom) )
     3130                        return new IXR_Error(0, __('The source URL and the target URL cannot both point to the same resource.'));
     3131
     3132                // Check if pings are on
     3133                if ( !pings_open($post) )
     3134                        return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.'));
     3135
     3136                // Let's check that the remote site didn't already pingback this entry
     3137                if ( $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_ID, $pagelinkedfrom) ) )
     3138                        return new IXR_Error( 48, __( 'The pingback has already been registered.' ) );
     3139
     3140                // very stupid, but gives time to the 'from' server to publish !
     3141                sleep(1);
     3142
     3143                // Let's check the remote site
     3144                $linea = wp_remote_fopen( $pagelinkedfrom );
     3145                if ( !$linea )
     3146                        return new IXR_Error(16, __('The source URL does not exist.'));
     3147
     3148                $linea = apply_filters('pre_remote_source', $linea, $pagelinkedto);
     3149
     3150                // Work around bug in strip_tags():
     3151                $linea = str_replace('<!DOC', '<DOC', $linea);
     3152                $linea = preg_replace( '/[\s\r\n\t]+/', ' ', $linea ); // normalize spaces
     3153                $linea = preg_replace( "/ <(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $linea );
     3154
     3155                preg_match('|<title>([^<]*?)</title>|is', $linea, $matchtitle);
     3156                $title = $matchtitle[1];
     3157                if ( empty( $title ) )
     3158                        return new IXR_Error(32, __('We cannot find a title on that page.'));
     3159
     3160                $linea = strip_tags( $linea, '<a>' ); // just keep the tag we need
     3161
     3162                $p = explode( "\n\n", $linea );
     3163
     3164                $preg_target = preg_quote($pagelinkedto, '|');
     3165
     3166                foreach ( $p as $para ) {
     3167                        if ( strpos($para, $pagelinkedto) !== false ) { // it exists, but is it a link?
     3168                                preg_match("|<a[^>]+?".$preg_target."[^>]*>([^>]+?)</a>|", $para, $context);
     3169
     3170                                // If the URL isn't in a link context, keep looking
     3171                                if ( empty($context) )
     3172                                        continue;
     3173
     3174                                // We're going to use this fake tag to mark the context in a bit
     3175                                // the marker is needed in case the link text appears more than once in the paragraph
     3176                                $excerpt = preg_replace('|\</?wpcontext\>|', '', $para);
     3177
     3178                                // prevent really long link text
     3179                                if ( strlen($context[1]) > 100 )
     3180                                        $context[1] = substr($context[1], 0, 100) . '...';
     3181
     3182                                $marker = '<wpcontext>'.$context[1].'</wpcontext>';    // set up our marker
     3183                                $excerpt= str_replace($context[0], $marker, $excerpt); // swap out the link for our marker
     3184                                $excerpt = strip_tags($excerpt, '<wpcontext>');        // strip all tags but our context marker
     3185                                $excerpt = trim($excerpt);
     3186                                $preg_marker = preg_quote($marker, '|');
     3187                                $excerpt = preg_replace("|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt);
     3188                                $excerpt = strip_tags($excerpt); // YES, again, to remove the marker wrapper
     3189                                break;
     3190                        }
     3191                }
     3192
     3193                if ( empty($context) ) // Link to target not found
     3194                        return new IXR_Error(17, __('The source URL does not contain a link to the target URL, and so cannot be used as a source.'));
     3195
     3196                $pagelinkedfrom = str_replace('&', '&amp;', $pagelinkedfrom);
     3197
     3198                $context = '[...] ' . esc_html( $excerpt ) . ' [...]';
     3199                $pagelinkedfrom = $wpdb->escape( $pagelinkedfrom );
     3200
     3201                $comment_post_ID = (int) $post_ID;
     3202                $comment_author = $title;
     3203                $this->escape($comment_author);
     3204                $comment_author_url = $pagelinkedfrom;
     3205                $comment_content = $context;
     3206                $this->escape($comment_content);
     3207                $comment_type = 'pingback';
     3208
     3209                $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_content', 'comment_type');
     3210
     3211                $comment_ID = wp_new_comment($commentdata);
     3212                do_action('pingback_post', $comment_ID);
     3213
     3214                return sprintf(__('Pingback from %1$s to %2$s registered. Keep the web talking! :-)'), $pagelinkedfrom, $pagelinkedto);
     3215        }
     3216
     3217        /**
     3218         * Retrieve array of URLs that pingbacked the given URL.
     3219         *
     3220         * Specs on http://www.aquarionics.com/misc/archives/blogite/0198.html
     3221         *
     3222         * @since 1.5.0
     3223         *
     3224         * @param array $args Method parameters.
     3225         * @return array
     3226         */
     3227        function pingback_extensions_getPingbacks($args) {
     3228
     3229                global $wpdb;
     3230
     3231                do_action('xmlrpc_call', 'pingback.extensions.getPingbacks');
     3232
     3233                $this->escape($args);
     3234
     3235                $url = $args;
     3236
     3237                $post_ID = url_to_postid($url);
     3238                if ( !$post_ID ) {
     3239                        // We aren't sure that the resource is available and/or pingback enabled
     3240                        return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.'));
     3241                }
     3242
     3243                $actual_post = wp_get_single_post($post_ID, ARRAY_A);
     3244
     3245                if ( !$actual_post ) {
     3246                        // No such post = resource not found
     3247                        return new IXR_Error(32, __('The specified target URL does not exist.'));
     3248                }
     3249
     3250                $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_ID) );
     3251
     3252                if ( !$comments )
     3253                        return array();
     3254
     3255                $pingbacks = array();
     3256                foreach ( $comments as $comment ) {
     3257                        if ( 'pingback' == $comment->comment_type )
     3258                                $pingbacks[] = $comment->comment_author_url;
     3259                }
     3260
     3261                return $pingbacks;
     3262        }
     3263}
  • xmlrpc.php

     
    5252
    5353include_once(ABSPATH . 'wp-admin/includes/admin.php');
    5454include_once(ABSPATH . WPINC . '/class-IXR.php');
     55include_once(ABSPATH . WPINC . '/class-xmlrpc-server.php');
    5556
    5657// Turn off all warnings and errors.
    5758// error_reporting(0);
     
    7172 */
    7273$xmlrpc_logging = 0;
    7374
    74 /**
    75  * logIO() - Writes logging info to a file.
    76  *
    77  * @uses $xmlrpc_logging
    78  * @package WordPress
    79  * @subpackage Logging
    80  *
    81  * @param string $io Whether input or output
    82  * @param string $msg Information describing logging reason.
    83  * @return bool Always return true
    84  */
    85 function logIO($io,$msg) {
    86         global $xmlrpc_logging;
    87         if ($xmlrpc_logging) {
    88                 $fp = fopen("../xmlrpc.log","a+");
    89                 $date = gmdate("Y-m-d H:i:s ");
    90                 $iot = ($io == "I") ? " Input: " : " Output: ";
    91                 fwrite($fp, "\n\n".$date.$iot.$msg);
    92                 fclose($fp);
    93         }
    94         return true;
    95 }
    96 
    9775if ( isset($HTTP_RAW_POST_DATA) )
    9876        logIO("I", $HTTP_RAW_POST_DATA);
    9977
    100 /**
    101  * WordPress XMLRPC server implementation.
    102  *
    103  * Implements compatability for Blogger API, MetaWeblog API, MovableType, and
    104  * pingback. Additional WordPress API for managing comments, pages, posts,
    105  * options, etc.
    106  *
    107  * Since WordPress 2.6.0, WordPress XMLRPC server can be disabled in the
    108  * administration panels.
    109  *
    110  * @package WordPress
    111  * @subpackage Publishing
    112  * @since 1.5.0
    113  */
    114 class wp_xmlrpc_server extends IXR_Server {
    115 
    116         /**
    117          * Register all of the XMLRPC methods that XMLRPC server understands.
    118          *
    119          * PHP4 constructor and sets up server and method property. Passes XMLRPC
    120          * methods through the 'xmlrpc_methods' filter to allow plugins to extend
    121          * or replace XMLRPC methods.
    122          *
    123          * @since 1.5.0
    124          *
    125          * @return wp_xmlrpc_server
    126          */
    127         function wp_xmlrpc_server() {
    128                 $this->methods = array(
    129                         // WordPress API
    130                         'wp.getUsersBlogs'              => 'this:wp_getUsersBlogs',
    131                         'wp.getPage'                    => 'this:wp_getPage',
    132                         'wp.getPages'                   => 'this:wp_getPages',
    133                         'wp.newPage'                    => 'this:wp_newPage',
    134                         'wp.deletePage'                 => 'this:wp_deletePage',
    135                         'wp.editPage'                   => 'this:wp_editPage',
    136                         'wp.getPageList'                => 'this:wp_getPageList',
    137                         'wp.getAuthors'                 => 'this:wp_getAuthors',
    138                         'wp.getCategories'              => 'this:mw_getCategories',             // Alias
    139                         'wp.getTags'                    => 'this:wp_getTags',
    140                         'wp.newCategory'                => 'this:wp_newCategory',
    141                         'wp.deleteCategory'             => 'this:wp_deleteCategory',
    142                         'wp.suggestCategories'  => 'this:wp_suggestCategories',
    143                         'wp.uploadFile'                 => 'this:mw_newMediaObject',    // Alias
    144                         'wp.getCommentCount'    => 'this:wp_getCommentCount',
    145                         'wp.getPostStatusList'  => 'this:wp_getPostStatusList',
    146                         'wp.getPageStatusList'  => 'this:wp_getPageStatusList',
    147                         'wp.getPageTemplates'   => 'this:wp_getPageTemplates',
    148                         'wp.getOptions'                 => 'this:wp_getOptions',
    149                         'wp.setOptions'                 => 'this:wp_setOptions',
    150                         'wp.getComment'                 => 'this:wp_getComment',
    151                         'wp.getComments'                => 'this:wp_getComments',
    152                         'wp.deleteComment'              => 'this:wp_deleteComment',
    153                         'wp.editComment'                => 'this:wp_editComment',
    154                         'wp.newComment'                 => 'this:wp_newComment',
    155                         'wp.getCommentStatusList' => 'this:wp_getCommentStatusList',
    156 
    157                         // Blogger API
    158                         'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',
    159                         'blogger.getUserInfo' => 'this:blogger_getUserInfo',
    160                         'blogger.getPost' => 'this:blogger_getPost',
    161                         'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',
    162                         'blogger.getTemplate' => 'this:blogger_getTemplate',
    163                         'blogger.setTemplate' => 'this:blogger_setTemplate',
    164                         'blogger.newPost' => 'this:blogger_newPost',
    165                         'blogger.editPost' => 'this:blogger_editPost',
    166                         'blogger.deletePost' => 'this:blogger_deletePost',
    167 
    168                         // MetaWeblog API (with MT extensions to structs)
    169                         'metaWeblog.newPost' => 'this:mw_newPost',
    170                         'metaWeblog.editPost' => 'this:mw_editPost',
    171                         'metaWeblog.getPost' => 'this:mw_getPost',
    172                         'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts',
    173                         'metaWeblog.getCategories' => 'this:mw_getCategories',
    174                         'metaWeblog.newMediaObject' => 'this:mw_newMediaObject',
    175 
    176                         // MetaWeblog API aliases for Blogger API
    177                         // see http://www.xmlrpc.com/stories/storyReader$2460
    178                         'metaWeblog.deletePost' => 'this:blogger_deletePost',
    179                         'metaWeblog.getTemplate' => 'this:blogger_getTemplate',
    180                         'metaWeblog.setTemplate' => 'this:blogger_setTemplate',
    181                         'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',
    182 
    183                         // MovableType API
    184                         'mt.getCategoryList' => 'this:mt_getCategoryList',
    185                         'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles',
    186                         'mt.getPostCategories' => 'this:mt_getPostCategories',
    187                         'mt.setPostCategories' => 'this:mt_setPostCategories',
    188                         'mt.supportedMethods' => 'this:mt_supportedMethods',
    189                         'mt.supportedTextFilters' => 'this:mt_supportedTextFilters',
    190                         'mt.getTrackbackPings' => 'this:mt_getTrackbackPings',
    191                         'mt.publishPost' => 'this:mt_publishPost',
    192 
    193                         // PingBack
    194                         'pingback.ping' => 'this:pingback_ping',
    195                         'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks',
    196 
    197                         'demo.sayHello' => 'this:sayHello',
    198                         'demo.addTwoNumbers' => 'this:addTwoNumbers'
    199                 );
    200 
    201                 $this->initialise_blog_option_info( );
    202                 $this->methods = apply_filters('xmlrpc_methods', $this->methods);
    203         }
    204 
    205         function serve_request() {
    206                 $this->IXR_Server($this->methods);
    207         }
    208 
    209         /**
    210          * Test XMLRPC API by saying, "Hello!" to client.
    211          *
    212          * @since 1.5.0
    213          *
    214          * @param array $args Method Parameters.
    215          * @return string
    216          */
    217         function sayHello($args) {
    218                 return 'Hello!';
    219         }
    220 
    221         /**
    222          * Test XMLRPC API by adding two numbers for client.
    223          *
    224          * @since 1.5.0
    225          *
    226          * @param array $args Method Parameters.
    227          * @return int
    228          */
    229         function addTwoNumbers($args) {
    230                 $number1 = $args[0];
    231                 $number2 = $args[1];
    232                 return $number1 + $number2;
    233         }
    234 
    235         /**
    236          * Check user's credentials.
    237          *
    238          * @since 1.5.0
    239          *
    240          * @param string $user_login User's username.
    241          * @param string $user_pass User's password.
    242          * @return bool Whether authentication passed.
    243          * @deprecated use wp_xmlrpc_server::login
    244          * @see wp_xmlrpc_server::login
    245          */
    246         function login_pass_ok($user_login, $user_pass) {
    247                 if ( !get_option( 'enable_xmlrpc' ) ) {
    248                         $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site.  An admin user can enable them at %s'),  admin_url('options-writing.php') ) );
    249                         return false;
    250                 }
    251 
    252                 if (!user_pass_ok($user_login, $user_pass)) {
    253                         $this->error = new IXR_Error(403, __('Bad login/pass combination.'));
    254                         return false;
    255                 }
    256                 return true;
    257         }
    258 
    259         /**
    260          * Log user in.
    261          *
    262          * @since 2.8
    263          *
    264          * @param string $username User's username.
    265          * @param string $password User's password.
    266          * @return mixed WP_User object if authentication passed, false otherwise
    267          */
    268         function login($username, $password) {
    269                 if ( !get_option( 'enable_xmlrpc' ) ) {
    270                         $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site.  An admin user can enable them at %s'),  admin_url('options-writing.php') ) );
    271                         return false;
    272                 }
    273 
    274                 $user = wp_authenticate($username, $password);
    275 
    276                 if (is_wp_error($user)) {
    277                         $this->error = new IXR_Error(403, __('Bad login/pass combination.'));
    278                         return false;
    279                 }
    280 
    281                 wp_set_current_user( $user->ID );
    282                 return $user;
    283         }
    284 
    285         /**
    286          * Sanitize string or array of strings for database.
    287          *
    288          * @since 1.5.2
    289          *
    290          * @param string|array $array Sanitize single string or array of strings.
    291          * @return string|array Type matches $array and sanitized for the database.
    292          */
    293         function escape(&$array) {
    294                 global $wpdb;
    295 
    296                 if (!is_array($array)) {
    297                         return($wpdb->escape($array));
    298                 } else {
    299                         foreach ( (array) $array as $k => $v ) {
    300                                 if ( is_array($v) ) {
    301                                         $this->escape($array[$k]);
    302                                 } else if ( is_object($v) ) {
    303                                         //skip
    304                                 } else {
    305                                         $array[$k] = $wpdb->escape($v);
    306                                 }
    307                         }
    308                 }
    309         }
    310 
    311         /**
    312          * Retrieve custom fields for post.
    313          *
    314          * @since 2.5.0
    315          *
    316          * @param int $post_id Post ID.
    317          * @return array Custom fields, if exist.
    318          */
    319         function get_custom_fields($post_id) {
    320                 $post_id = (int) $post_id;
    321 
    322                 $custom_fields = array();
    323 
    324                 foreach ( (array) has_meta($post_id) as $meta ) {
    325                         // Don't expose protected fields.
    326                         if ( strpos($meta['meta_key'], '_wp_') === 0 ) {
    327                                 continue;
    328                         }
    329 
    330                         $custom_fields[] = array(
    331                                 "id"    => $meta['meta_id'],
    332                                 "key"   => $meta['meta_key'],
    333                                 "value" => $meta['meta_value']
    334                         );
    335                 }
    336 
    337                 return $custom_fields;
    338         }
    339 
    340         /**
    341          * Set custom fields for post.
    342          *
    343          * @since 2.5.0
    344          *
    345          * @param int $post_id Post ID.
    346          * @param array $fields Custom fields.
    347          */
    348         function set_custom_fields($post_id, $fields) {
    349                 $post_id = (int) $post_id;
    350 
    351                 foreach ( (array) $fields as $meta ) {
    352                         if ( isset($meta['id']) ) {
    353                                 $meta['id'] = (int) $meta['id'];
    354 
    355                                 if ( isset($meta['key']) ) {
    356                                         update_meta($meta['id'], $meta['key'], $meta['value']);
    357                                 }
    358                                 else {
    359                                         delete_meta($meta['id']);
    360                                 }
    361                         }
    362                         else {
    363                                 $_POST['metakeyinput'] = $meta['key'];
    364                                 $_POST['metavalue'] = $meta['value'];
    365                                 add_meta($post_id);
    366                         }
    367                 }
    368         }
    369 
    370         /**
    371          * Set up blog options property.
    372          *
    373          * Passes property through 'xmlrpc_blog_options' filter.
    374          *
    375          * @since 2.6.0
    376          */
    377         function initialise_blog_option_info( ) {
    378                 global $wp_version;
    379 
    380                 $this->blog_options = array(
    381                         // Read only options
    382                         'software_name'         => array(
    383                                 'desc'                  => __( 'Software Name' ),
    384                                 'readonly'              => true,
    385                                 'value'                 => 'WordPress'
    386                         ),
    387                         'software_version'      => array(
    388                                 'desc'                  => __( 'Software Version' ),
    389                                 'readonly'              => true,
    390                                 'value'                 => $wp_version
    391                         ),
    392                         'blog_url'                      => array(
    393                                 'desc'                  => __( 'Site URL' ),
    394                                 'readonly'              => true,
    395                                 'option'                => 'siteurl'
    396                         ),
    397 
    398                         // Updatable options
    399                         'time_zone'                     => array(
    400                                 'desc'                  => __( 'Time Zone' ),
    401                                 'readonly'              => false,
    402                                 'option'                => 'gmt_offset'
    403                         ),
    404                         'blog_title'            => array(
    405                                 'desc'                  => __( 'Site Title' ),
    406                                 'readonly'              => false,
    407                                 'option'                        => 'blogname'
    408                         ),
    409                         'blog_tagline'          => array(
    410                                 'desc'                  => __( 'Site Tagline' ),
    411                                 'readonly'              => false,
    412                                 'option'                => 'blogdescription'
    413                         ),
    414                         'date_format'           => array(
    415                                 'desc'                  => __( 'Date Format' ),
    416                                 'readonly'              => false,
    417                                 'option'                => 'date_format'
    418                         ),
    419                         'time_format'           => array(
    420                                 'desc'                  => __( 'Time Format' ),
    421                                 'readonly'              => false,
    422                                 'option'                => 'time_format'
    423                         ),
    424                         'users_can_register'    => array(
    425                                 'desc'                  => __( 'Allow new users to sign up' ),
    426                                 'readonly'              => false,
    427                                 'option'                => 'users_can_register'
    428                         )
    429                 );
    430 
    431                 $this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options );
    432         }
    433 
    434         /**
    435          * Retrieve the blogs of the user.
    436          *
    437          * @since 2.6.0
    438          *
    439          * @param array $args Method parameters.
    440          * @return array
    441          */
    442         function wp_getUsersBlogs( $args ) {
    443                 global $current_site;
    444                 // If this isn't on WPMU then just use blogger_getUsersBlogs
    445                 if ( !is_multisite() ) {
    446                         array_unshift( $args, 1 );
    447                         return $this->blogger_getUsersBlogs( $args );
    448                 }
    449 
    450                 $this->escape( $args );
    451 
    452                 $username = $args[0];
    453                 $password = $args[1];
    454 
    455                 if ( !$user = $this->login($username, $password) )
    456                         return $this->error;
    457 
    458 
    459                 do_action( 'xmlrpc_call', 'wp.getUsersBlogs' );
    460 
    461                 $blogs = (array) get_blogs_of_user( $user->ID );
    462                 $struct = array( );
    463 
    464                 foreach ( $blogs as $blog ) {
    465                         // Don't include blogs that aren't hosted at this site
    466                         if ( $blog->site_id != $current_site->id )
    467                                 continue;
    468 
    469                         $blog_id = $blog->userblog_id;
    470                         switch_to_blog($blog_id);
    471                         $is_admin = current_user_can('manage_options');
    472 
    473                         $struct[] = array(
    474                                 'isAdmin'               => $is_admin,
    475                                 'url'                   => get_option( 'home' ) . '/',
    476                                 'blogid'                => $blog_id,
    477                                 'blogName'              => get_option( 'blogname' ),
    478                                 'xmlrpc'                => site_url( 'xmlrpc.php' )
    479                         );
    480 
    481                         restore_current_blog( );
    482                 }
    483 
    484                 return $struct;
    485         }
    486 
    487         /**
    488          * Retrieve page.
    489          *
    490          * @since 2.2.0
    491          *
    492          * @param array $args Method parameters.
    493          * @return array
    494          */
    495         function wp_getPage($args) {
    496                 $this->escape($args);
    497 
    498                 $blog_id        = (int) $args[0];
    499                 $page_id        = (int) $args[1];
    500                 $username       = $args[2];
    501                 $password       = $args[3];
    502 
    503                 if ( !$user = $this->login($username, $password) ) {
    504                         return $this->error;
    505                 }
    506 
    507                 if ( !current_user_can( 'edit_page', $page_id ) )
    508                         return new IXR_Error( 401, __( 'Sorry, you cannot edit this page.' ) );
    509 
    510                 do_action('xmlrpc_call', 'wp.getPage');
    511 
    512                 // Lookup page info.
    513                 $page = get_page($page_id);
    514 
    515                 // If we found the page then format the data.
    516                 if ( $page->ID && ($page->post_type == "page") ) {
    517                         // Get all of the page content and link.
    518                         $full_page = get_extended($page->post_content);
    519                         $link = post_permalink($page->ID);
    520 
    521                         // Get info the page parent if there is one.
    522                         $parent_title = "";
    523                         if ( !empty($page->post_parent) ) {
    524                                 $parent = get_page($page->post_parent);
    525                                 $parent_title = $parent->post_title;
    526                         }
    527 
    528                         // Determine comment and ping settings.
    529                         $allow_comments = comments_open($page->ID) ? 1 : 0;
    530                         $allow_pings = pings_open($page->ID) ? 1 : 0;
    531 
    532                         // Format page date.
    533                         $page_date = mysql2date("Ymd\TH:i:s", $page->post_date, false);
    534                         $page_date_gmt = mysql2date("Ymd\TH:i:s", $page->post_date_gmt, false);
    535 
    536                         // For drafts use the GMT version of the date
    537                         if ( $page->post_status == 'draft' )
    538                                 $page_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $page->post_date ), 'Ymd\TH:i:s' );
    539 
    540                         // Pull the categories info together.
    541                         $categories = array();
    542                         foreach ( wp_get_post_categories($page->ID) as $cat_id ) {
    543                                 $categories[] = get_cat_name($cat_id);
    544                         }
    545 
    546                         // Get the author info.
    547                         $author = get_userdata($page->post_author);
    548 
    549                         $page_template = get_post_meta( $page->ID, '_wp_page_template', true );
    550                         if ( empty( $page_template ) )
    551                                 $page_template = 'default';
    552 
    553                         $page_struct = array(
    554                                 "dateCreated"                   => new IXR_Date($page_date),
    555                                 "userid"                                => $page->post_author,
    556                                 "page_id"                               => $page->ID,
    557                                 "page_status"                   => $page->post_status,
    558                                 "description"                   => $full_page["main"],
    559                                 "title"                                 => $page->post_title,
    560                                 "link"                                  => $link,
    561                                 "permaLink"                             => $link,
    562                                 "categories"                    => $categories,
    563                                 "excerpt"                               => $page->post_excerpt,
    564                                 "text_more"                             => $full_page["extended"],
    565                                 "mt_allow_comments"             => $allow_comments,
    566                                 "mt_allow_pings"                => $allow_pings,
    567                                 "wp_slug"                               => $page->post_name,
    568                                 "wp_password"                   => $page->post_password,
    569                                 "wp_author"                             => $author->display_name,
    570                                 "wp_page_parent_id"             => $page->post_parent,
    571                                 "wp_page_parent_title"  => $parent_title,
    572                                 "wp_page_order"                 => $page->menu_order,
    573                                 "wp_author_id"                  => $author->ID,
    574                                 "wp_author_display_name"        => $author->display_name,
    575                                 "date_created_gmt"              => new IXR_Date($page_date_gmt),
    576                                 "custom_fields"                 => $this->get_custom_fields($page_id),
    577                                 "wp_page_template"              => $page_template
    578                         );
    579 
    580                         return($page_struct);
    581                 }
    582                 // If the page doesn't exist indicate that.
    583                 else {
    584                         return(new IXR_Error(404, __("Sorry, no such page.")));
    585                 }
    586         }
    587 
    588         /**
    589          * Retrieve Pages.
    590          *
    591          * @since 2.2.0
    592          *
    593          * @param array $args Method parameters.
    594          * @return array
    595          */
    596         function wp_getPages($args) {
    597                 $this->escape($args);
    598 
    599                 $blog_id        = (int) $args[0];
    600                 $username       = $args[1];
    601                 $password       = $args[2];
    602                 $num_pages      = isset($args[3]) ? (int) $args[3] : 10;
    603 
    604                 if ( !$user = $this->login($username, $password) )
    605                         return $this->error;
    606 
    607                 if ( !current_user_can( 'edit_pages' ) )
    608                         return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) );
    609 
    610                 do_action('xmlrpc_call', 'wp.getPages');
    611 
    612                 $pages = get_posts( array('post_type' => 'page', 'post_status' => 'any', 'numberposts' => $num_pages) );
    613                 $num_pages = count($pages);
    614 
    615                 // If we have pages, put together their info.
    616                 if ( $num_pages >= 1 ) {
    617                         $pages_struct = array();
    618 
    619                         for ( $i = 0; $i < $num_pages; $i++ ) {
    620                                 $page = wp_xmlrpc_server::wp_getPage(array(
    621                                         $blog_id, $pages[$i]->ID, $username, $password
    622                                 ));
    623                                 $pages_struct[] = $page;
    624                         }
    625 
    626                         return($pages_struct);
    627                 }
    628                 // If no pages were found return an error.
    629                 else {
    630                         return(array());
    631                 }
    632         }
    633 
    634         /**
    635          * Create new page.
    636          *
    637          * @since 2.2.0
    638          *
    639          * @param array $args Method parameters.
    640          * @return unknown
    641          */
    642         function wp_newPage($args) {
    643                 // Items not escaped here will be escaped in newPost.
    644                 $username       = $this->escape($args[1]);
    645                 $password       = $this->escape($args[2]);
    646                 $page           = $args[3];
    647                 $publish        = $args[4];
    648 
    649                 if ( !$user = $this->login($username, $password) )
    650                         return $this->error;
    651 
    652                 do_action('xmlrpc_call', 'wp.newPage');
    653 
    654                 // Make sure the user is allowed to add new pages.
    655                 if ( !current_user_can("publish_pages") )
    656                         return(new IXR_Error(401, __("Sorry, you cannot add new pages.")));
    657 
    658                 // Mark this as content for a page.
    659                 $args[3]["post_type"] = "page";
    660 
    661                 // Let mw_newPost do all of the heavy lifting.
    662                 return($this->mw_newPost($args));
    663         }
    664 
    665         /**
    666          * Delete page.
    667          *
    668          * @since 2.2.0
    669          *
    670          * @param array $args Method parameters.
    671          * @return bool True, if success.
    672          */
    673         function wp_deletePage($args) {
    674                 $this->escape($args);
    675 
    676                 $blog_id        = (int) $args[0];
    677                 $username       = $args[1];
    678                 $password       = $args[2];
    679                 $page_id        = (int) $args[3];
    680 
    681                 if ( !$user = $this->login($username, $password) )
    682                         return $this->error;
    683 
    684                 do_action('xmlrpc_call', 'wp.deletePage');
    685 
    686                 // Get the current page based on the page_id and
    687                 // make sure it is a page and not a post.
    688                 $actual_page = wp_get_single_post($page_id, ARRAY_A);
    689                 if ( !$actual_page || ($actual_page["post_type"] != "page") )
    690                         return(new IXR_Error(404, __("Sorry, no such page.")));
    691 
    692                 // Make sure the user can delete pages.
    693                 if ( !current_user_can("delete_page", $page_id) )
    694                         return(new IXR_Error(401, __("Sorry, you do not have the right to delete this page.")));
    695 
    696                 // Attempt to delete the page.
    697                 $result = wp_delete_post($page_id);
    698                 if ( !$result )
    699                         return(new IXR_Error(500, __("Failed to delete the page.")));
    700 
    701                 return(true);
    702         }
    703 
    704         /**
    705          * Edit page.
    706          *
    707          * @since 2.2.0
    708          *
    709          * @param array $args Method parameters.
    710          * @return unknown
    711          */
    712         function wp_editPage($args) {
    713                 // Items not escaped here will be escaped in editPost.
    714                 $blog_id        = (int) $args[0];
    715                 $page_id        = (int) $this->escape($args[1]);
    716                 $username       = $this->escape($args[2]);
    717                 $password       = $this->escape($args[3]);
    718                 $content        = $args[4];
    719                 $publish        = $args[5];
    720 
    721                 if ( !$user = $this->login($username, $password) )
    722                         return $this->error;
    723 
    724                 do_action('xmlrpc_call', 'wp.editPage');
    725 
    726                 // Get the page data and make sure it is a page.
    727                 $actual_page = wp_get_single_post($page_id, ARRAY_A);
    728                 if ( !$actual_page || ($actual_page["post_type"] != "page") )
    729                         return(new IXR_Error(404, __("Sorry, no such page.")));
    730 
    731                 // Make sure the user is allowed to edit pages.
    732                 if ( !current_user_can("edit_page", $page_id) )
    733                         return(new IXR_Error(401, __("Sorry, you do not have the right to edit this page.")));
    734 
    735                 // Mark this as content for a page.
    736                 $content["post_type"] = "page";
    737 
    738                 // Arrange args in the way mw_editPost understands.
    739                 $args = array(
    740                         $page_id,
    741                         $username,
    742                         $password,
    743                         $content,
    744                         $publish
    745                 );
    746 
    747                 // Let mw_editPost do all of the heavy lifting.
    748                 return($this->mw_editPost($args));
    749         }
    750 
    751         /**
    752          * Retrieve page list.
    753          *
    754          * @since 2.2.0
    755          *
    756          * @param array $args Method parameters.
    757          * @return unknown
    758          */
    759         function wp_getPageList($args) {
    760                 global $wpdb;
    761 
    762                 $this->escape($args);
    763 
    764                 $blog_id                                = (int) $args[0];
    765                 $username                               = $args[1];
    766                 $password                               = $args[2];
    767 
    768                 if ( !$user = $this->login($username, $password) )
    769                         return $this->error;
    770 
    771                 if ( !current_user_can( 'edit_pages' ) )
    772                         return new IXR_Error( 401, __( 'Sorry, you cannot edit pages.' ) );
    773 
    774                 do_action('xmlrpc_call', 'wp.getPageList');
    775 
    776                 // Get list of pages ids and titles
    777                 $page_list = $wpdb->get_results("
    778                         SELECT ID page_id,
    779                                 post_title page_title,
    780                                 post_parent page_parent_id,
    781                                 post_date_gmt,
    782                                 post_date,
    783                                 post_status
    784                         FROM {$wpdb->posts}
    785                         WHERE post_type = 'page'
    786                         ORDER BY ID
    787                 ");
    788 
    789                 // The date needs to be formated properly.
    790                 $num_pages = count($page_list);
    791                 for ( $i = 0; $i < $num_pages; $i++ ) {
    792                         $post_date = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date, false);
    793                         $post_date_gmt = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date_gmt, false);
    794 
    795                         $page_list[$i]->dateCreated = new IXR_Date($post_date);
    796                         $page_list[$i]->date_created_gmt = new IXR_Date($post_date_gmt);
    797 
    798                         // For drafts use the GMT version of the date
    799                         if ( $page_list[$i]->post_status == 'draft' ) {
    800                                 $page_list[$i]->date_created_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $page_list[$i]->post_date ), 'Ymd\TH:i:s' );
    801                                 $page_list[$i]->date_created_gmt = new IXR_Date( $page_list[$i]->date_created_gmt );
    802                         }
    803 
    804                         unset($page_list[$i]->post_date_gmt);
    805                         unset($page_list[$i]->post_date);
    806                         unset($page_list[$i]->post_status);
    807                 }
    808 
    809                 return($page_list);
    810         }
    811 
    812         /**
    813          * Retrieve authors list.
    814          *
    815          * @since 2.2.0
    816          *
    817          * @param array $args Method parameters.
    818          * @return array
    819          */
    820         function wp_getAuthors($args) {
    821 
    822                 $this->escape($args);
    823 
    824                 $blog_id        = (int) $args[0];
    825                 $username       = $args[1];
    826                 $password       = $args[2];
    827 
    828                 if ( !$user = $this->login($username, $password) )
    829                         return $this->error;
    830 
    831                 if ( !current_user_can("edit_posts") )
    832                         return(new IXR_Error(401, __("Sorry, you cannot edit posts on this site.")));
    833 
    834                 do_action('xmlrpc_call', 'wp.getAuthors');
    835 
    836                 $authors = array();
    837                 foreach ( get_users() as $user_id => $user_object ) {
    838                         $authors[] = array(
    839                                 "user_id"       => $user_id,
    840                                 "user_login"    => $user_object->user_login,
    841                                 "display_name"  => $user_object->display_name
    842                         );
    843                 }
    844 
    845                 return $authors;
    846         }
    847 
    848         /**
    849          * Get list of all tags
    850          *
    851          * @since 2.7
    852          *
    853          * @param array $args Method parameters.
    854          * @return array
    855          */
    856         function wp_getTags( $args ) {
    857                 $this->escape( $args );
    858 
    859                 $blog_id                = (int) $args[0];
    860                 $username               = $args[1];
    861                 $password               = $args[2];
    862 
    863                 if ( !$user = $this->login($username, $password) )
    864                         return $this->error;
    865 
    866                 if ( !current_user_can( 'edit_posts' ) )
    867                         return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view tags.' ) );
    868 
    869                 do_action( 'xmlrpc_call', 'wp.getKeywords' );
    870 
    871                 $tags = array( );
    872 
    873                 if ( $all_tags = get_tags() ) {
    874                         foreach( (array) $all_tags as $tag ) {
    875                                 $struct['tag_id']                       = $tag->term_id;
    876                                 $struct['name']                         = $tag->name;
    877                                 $struct['count']                        = $tag->count;
    878                                 $struct['slug']                         = $tag->slug;
    879                                 $struct['html_url']                     = esc_html( get_tag_link( $tag->term_id ) );
    880                                 $struct['rss_url']                      = esc_html( get_tag_feed_link( $tag->term_id ) );
    881 
    882                                 $tags[] = $struct;
    883                         }
    884                 }
    885 
    886                 return $tags;
    887         }
    888 
    889         /**
    890          * Create new category.
    891          *
    892          * @since 2.2.0
    893          *
    894          * @param array $args Method parameters.
    895          * @return int Category ID.
    896          */
    897         function wp_newCategory($args) {
    898                 $this->escape($args);
    899 
    900                 $blog_id                                = (int) $args[0];
    901                 $username                               = $args[1];
    902                 $password                               = $args[2];
    903                 $category                               = $args[3];
    904 
    905                 if ( !$user = $this->login($username, $password) )
    906                         return $this->error;
    907 
    908                 do_action('xmlrpc_call', 'wp.newCategory');
    909 
    910                 // Make sure the user is allowed to add a category.
    911                 if ( !current_user_can("manage_categories") )
    912                         return(new IXR_Error(401, __("Sorry, you do not have the right to add a category.")));
    913 
    914                 // If no slug was provided make it empty so that
    915                 // WordPress will generate one.
    916                 if ( empty($category["slug"]) )
    917                         $category["slug"] = "";
    918 
    919                 // If no parent_id was provided make it empty
    920                 // so that it will be a top level page (no parent).
    921                 if ( !isset($category["parent_id"]) )
    922                         $category["parent_id"] = "";
    923 
    924                 // If no description was provided make it empty.
    925                 if ( empty($category["description"]) )
    926                         $category["description"] = "";
    927 
    928                 $new_category = array(
    929                         "cat_name"                              => $category["name"],
    930                         "category_nicename"             => $category["slug"],
    931                         "category_parent"               => $category["parent_id"],
    932                         "category_description"  => $category["description"]
    933                 );
    934 
    935                 $cat_id = wp_insert_category($new_category, true);
    936                 if ( is_wp_error( $cat_id ) ) {
    937                         if ( 'term_exists' == $cat_id->get_error_code() )
    938                                 return (int) $cat_id->get_error_data();
    939                         else
    940                                 return(new IXR_Error(500, __("Sorry, the new category failed.")));
    941                 } elseif ( ! $cat_id ) {
    942                         return(new IXR_Error(500, __("Sorry, the new category failed.")));
    943                 }
    944 
    945                 return($cat_id);
    946         }
    947 
    948         /**
    949          * Remove category.
    950          *
    951          * @since 2.5.0
    952          *
    953          * @param array $args Method parameters.
    954          * @return mixed See {@link wp_delete_category()} for return info.
    955          */
    956         function wp_deleteCategory($args) {
    957                 $this->escape($args);
    958 
    959                 $blog_id                = (int) $args[0];
    960                 $username               = $args[1];
    961                 $password               = $args[2];
    962                 $category_id    = (int) $args[3];
    963 
    964                 if ( !$user = $this->login($username, $password) )
    965                         return $this->error;
    966 
    967                 do_action('xmlrpc_call', 'wp.deleteCategory');
    968 
    969                 if ( !current_user_can("manage_categories") )
    970                         return new IXR_Error( 401, __( "Sorry, you do not have the right to delete a category." ) );
    971 
    972                 return wp_delete_category( $category_id );
    973         }
    974 
    975         /**
    976          * Retrieve category list.
    977          *
    978          * @since 2.2.0
    979          *
    980          * @param array $args Method parameters.
    981          * @return array
    982          */
    983         function wp_suggestCategories($args) {
    984                 $this->escape($args);
    985 
    986                 $blog_id                                = (int) $args[0];
    987                 $username                               = $args[1];
    988                 $password                               = $args[2];
    989                 $category                               = $args[3];
    990                 $max_results                    = (int) $args[4];
    991 
    992                 if ( !$user = $this->login($username, $password) )
    993                         return $this->error;
    994 
    995                 if ( !current_user_can( 'edit_posts' ) )
    996                         return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts to this site in order to view categories.' ) );
    997 
    998                 do_action('xmlrpc_call', 'wp.suggestCategories');
    999 
    1000                 $category_suggestions = array();
    1001                 $args = array('get' => 'all', 'number' => $max_results, 'name__like' => $category);
    1002                 foreach ( (array) get_categories($args) as $cat ) {
    1003                         $category_suggestions[] = array(
    1004                                 "category_id"   => $cat->cat_ID,
    1005                                 "category_name" => $cat->cat_name
    1006                         );
    1007                 }
    1008 
    1009                 return($category_suggestions);
    1010         }
    1011 
    1012         /**
    1013          * Retrieve comment.
    1014          *
    1015          * @since 2.7.0
    1016          *
    1017          * @param array $args Method parameters.
    1018          * @return array
    1019          */
    1020         function wp_getComment($args) {
    1021                 $this->escape($args);
    1022 
    1023                 $blog_id        = (int) $args[0];
    1024                 $username       = $args[1];
    1025                 $password       = $args[2];
    1026                 $comment_id     = (int) $args[3];
    1027 
    1028                 if ( !$user = $this->login($username, $password) )
    1029                         return $this->error;
    1030 
    1031                 if ( !current_user_can( 'moderate_comments' ) )
    1032                         return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
    1033 
    1034                 do_action('xmlrpc_call', 'wp.getComment');
    1035 
    1036                 if ( ! $comment = get_comment($comment_id) )
    1037                         return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
    1038 
    1039                 // Format page date.
    1040                 $comment_date = mysql2date("Ymd\TH:i:s", $comment->comment_date, false);
    1041                 $comment_date_gmt = mysql2date("Ymd\TH:i:s", $comment->comment_date_gmt, false);
    1042 
    1043                 if ( '0' == $comment->comment_approved )
    1044                         $comment_status = 'hold';
    1045                 else if ( 'spam' == $comment->comment_approved )
    1046                         $comment_status = 'spam';
    1047                 else if ( '1' == $comment->comment_approved )
    1048                         $comment_status = 'approve';
    1049                 else
    1050                         $comment_status = $comment->comment_approved;
    1051 
    1052                 $link = get_comment_link($comment);
    1053 
    1054                 $comment_struct = array(
    1055                         "date_created_gmt"              => new IXR_Date($comment_date_gmt),
    1056                         "user_id"                               => $comment->user_id,
    1057                         "comment_id"                    => $comment->comment_ID,
    1058                         "parent"                                => $comment->comment_parent,
    1059                         "status"                                => $comment_status,
    1060                         "content"                               => $comment->comment_content,
    1061                         "link"                                  => $link,
    1062                         "post_id"                               => $comment->comment_post_ID,
    1063                         "post_title"                    => get_the_title($comment->comment_post_ID),
    1064                         "author"                                => $comment->comment_author,
    1065                         "author_url"                    => $comment->comment_author_url,
    1066                         "author_email"                  => $comment->comment_author_email,
    1067                         "author_ip"                             => $comment->comment_author_IP,
    1068                         "type"                                  => $comment->comment_type,
    1069                 );
    1070 
    1071                 return $comment_struct;
    1072         }
    1073 
    1074         /**
    1075          * Retrieve comments.
    1076          *
    1077          * @since 2.7.0
    1078          *
    1079          * @param array $args Method parameters.
    1080          * @return array
    1081          */
    1082         function wp_getComments($args) {
    1083                 $raw_args = $args;
    1084                 $this->escape($args);
    1085 
    1086                 $blog_id        = (int) $args[0];
    1087                 $username       = $args[1];
    1088                 $password       = $args[2];
    1089                 $struct         = $args[3];
    1090 
    1091                 if ( !$user = $this->login($username, $password) )
    1092                         return $this->error;
    1093 
    1094                 if ( !current_user_can( 'moderate_comments' ) )
    1095                         return new IXR_Error( 401, __( 'Sorry, you cannot edit comments.' ) );
    1096 
    1097                 do_action('xmlrpc_call', 'wp.getComments');
    1098 
    1099                 if ( isset($struct['status']) )
    1100                         $status = $struct['status'];
    1101                 else
    1102                         $status = '';
    1103 
    1104                 $post_id = '';
    1105                 if ( isset($struct['post_id']) )
    1106                         $post_id = absint($struct['post_id']);
    1107 
    1108                 $offset = 0;
    1109                 if ( isset($struct['offset']) )
    1110                         $offset = absint($struct['offset']);
    1111 
    1112                 $number = 10;
    1113                 if ( isset($struct['number']) )
    1114                         $number = absint($struct['number']);
    1115 
    1116                 $comments = get_comments( array('status' => $status, 'post_id' => $post_id, 'offset' => $offset, 'number' => $number ) );
    1117                 $num_comments = count($comments);
    1118 
    1119                 if ( ! $num_comments )
    1120                         return array();
    1121 
    1122                 $comments_struct = array();
    1123 
    1124                 for ( $i = 0; $i < $num_comments; $i++ ) {
    1125                         $comment = wp_xmlrpc_server::wp_getComment(array(
    1126                                 $raw_args[0], $raw_args[1], $raw_args[2], $comments[$i]->comment_ID,
    1127                         ));
    1128                         $comments_struct[] = $comment;
    1129                 }
    1130 
    1131                 return $comments_struct;
    1132         }
    1133 
    1134         /**
    1135          * Remove comment.
    1136          *
    1137          * @since 2.7.0
    1138          *
    1139          * @param array $args Method parameters.
    1140          * @return mixed {@link wp_delete_comment()}
    1141          */
    1142         function wp_deleteComment($args) {
    1143                 $this->escape($args);
    1144 
    1145                 $blog_id        = (int) $args[0];
    1146                 $username       = $args[1];
    1147                 $password       = $args[2];
    1148                 $comment_ID     = (int) $args[3];
    1149 
    1150                 if ( !$user = $this->login($username, $password) )
    1151                         return $this->error;
    1152 
    1153                 if ( !current_user_can( 'moderate_comments' ) )
    1154                         return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
    1155 
    1156                 do_action('xmlrpc_call', 'wp.deleteComment');
    1157 
    1158                 if ( ! get_comment($comment_ID) )
    1159                         return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
    1160 
    1161                 return wp_delete_comment($comment_ID);
    1162         }
    1163 
    1164         /**
    1165          * Edit comment.
    1166          *
    1167          * @since 2.7.0
    1168          *
    1169          * @param array $args Method parameters.
    1170          * @return bool True, on success.
    1171          */
    1172         function wp_editComment($args) {
    1173                 $this->escape($args);
    1174 
    1175                 $blog_id        = (int) $args[0];
    1176                 $username       = $args[1];
    1177                 $password       = $args[2];
    1178                 $comment_ID     = (int) $args[3];
    1179                 $content_struct = $args[4];
    1180 
    1181                 if ( !$user = $this->login($username, $password) )
    1182                         return $this->error;
    1183 
    1184                 if ( !current_user_can( 'moderate_comments' ) )
    1185                         return new IXR_Error( 403, __( 'You are not allowed to moderate comments on this site.' ) );
    1186 
    1187                 do_action('xmlrpc_call', 'wp.editComment');
    1188 
    1189                 if ( ! get_comment($comment_ID) )
    1190                         return new IXR_Error( 404, __( 'Invalid comment ID.' ) );
    1191 
    1192                 if ( isset($content_struct['status']) ) {
    1193                         $statuses = get_comment_statuses();
    1194                         $statuses = array_keys($statuses);
    1195 
    1196                         if ( ! in_array($content_struct['status'], $statuses) )
    1197                                 return new IXR_Error( 401, __( 'Invalid comment status.' ) );
    1198                         $comment_approved = $content_struct['status'];
    1199                 }
    1200 
    1201                 // Do some timestamp voodoo
    1202                 if ( !empty( $content_struct['date_created_gmt'] ) ) {
    1203                         $dateCreated = str_replace( 'Z', '', $content_struct['date_created_gmt']->getIso() ) . 'Z'; // We know this is supposed to be GMT, so we're going to slap that Z on there by force
    1204                         $comment_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
    1205                         $comment_date_gmt = iso8601_to_datetime($dateCreated, GMT);
    1206                 }
    1207 
    1208                 if ( isset($content_struct['content']) )
    1209                         $comment_content = $content_struct['content'];
    1210 
    1211                 if ( isset($content_struct['author']) )
    1212                         $comment_author = $content_struct['author'];
    1213 
    1214                 if ( isset($content_struct['author_url']) )
    1215                         $comment_author_url = $content_struct['author_url'];
    1216 
    1217                 if ( isset($content_struct['author_email']) )
    1218                         $comment_author_email = $content_struct['author_email'];
    1219 
    1220                 // We've got all the data -- post it:
    1221                 $comment = compact('comment_ID', 'comment_content', 'comment_approved', 'comment_date', 'comment_date_gmt', 'comment_author', 'comment_author_email', 'comment_author_url');
    1222 
    1223                 $result = wp_update_comment($comment);
    1224                 if ( is_wp_error( $result ) )
    1225                         return new IXR_Error(500, $result->get_error_message());
    1226 
    1227                 if ( !$result )
    1228                         return new IXR_Error(500, __('Sorry, the comment could not be edited. Something wrong happened.'));
    1229 
    1230                 return true;
    1231         }
    1232 
    1233         /**
    1234          * Create new comment.
    1235          *
    1236          * @since 2.7.0
    1237          *
    1238          * @param array $args Method parameters.
    1239          * @return mixed {@link wp_new_comment()}
    1240          */
    1241         function wp_newComment($args) {
    1242                 global $wpdb;
    1243 
    1244                 $this->escape($args);
    1245 
    1246                 $blog_id        = (int) $args[0];
    1247                 $username       = $args[1];
    1248                 $password       = $args[2];
    1249                 $post           = $args[3];
    1250                 $content_struct = $args[4];
    1251 
    1252                 $allow_anon = apply_filters('xmlrpc_allow_anonymous_comments', false);
    1253 
    1254                 $user = $this->login($username, $password);
    1255 
    1256                 if ( !$user ) {
    1257                         $logged_in = false;
    1258                         if ( $allow_anon && get_option('comment_registration') )
    1259                                 return new IXR_Error( 403, __( 'You must be registered to comment' ) );
    1260                         else if ( !$allow_anon )
    1261                                 return $this->error;
    1262                 } else {
    1263                         $logged_in = true;
    1264                 }
    1265 
    1266                 if ( is_numeric($post) )
    1267                         $post_id = absint($post);
    1268                 else
    1269                         $post_id = url_to_postid($post);
    1270 
    1271                 if ( ! $post_id )
    1272                         return new IXR_Error( 404, __( 'Invalid post ID.' ) );
    1273 
    1274                 if ( ! get_post($post_id) )
    1275                         return new IXR_Error( 404, __( 'Invalid post ID.' ) );
    1276 
    1277                 $comment['comment_post_ID'] = $post_id;
    1278 
    1279                 if ( $logged_in ) {
    1280                         $comment['comment_author'] = $wpdb->escape( $user->display_name );
    1281                         $comment['comment_author_email'] = $wpdb->escape( $user->user_email );
    1282                         $comment['comment_author_url'] = $wpdb->escape( $user->user_url );
    1283                         $comment['user_ID'] = $user->ID;
    1284                 } else {
    1285                         $comment['comment_author'] = '';
    1286                         if ( isset($content_struct['author']) )
    1287                                 $comment['comment_author'] = $content_struct['author'];
    1288 
    1289                         $comment['comment_author_email'] = '';
    1290                         if ( isset($content_struct['author_email']) )
    1291                                 $comment['comment_author_email'] = $content_struct['author_email'];
    1292 
    1293                         $comment['comment_author_url'] = '';
    1294                         if ( isset($content_struct['author_url']) )
    1295                                 $comment['comment_author_url'] = $content_struct['author_url'];
    1296 
    1297                         $comment['user_ID'] = 0;
    1298 
    1299                         if ( get_option('require_name_email') ) {
    1300                                 if ( 6 > strlen($comment['comment_author_email']) || '' == $comment['comment_author'] )
    1301                                         return new IXR_Error( 403, __( 'Comment author name and email are required' ) );
    1302                                 elseif ( !is_email($comment['comment_author_email']) )
    1303                                         return new IXR_Error( 403, __( 'A valid email address is required' ) );
    1304                         }
    1305                 }
    1306 
    1307                 $comment['comment_parent'] = isset($content_struct['comment_parent']) ? absint($content_struct['comment_parent']) : 0;
    1308 
    1309                 $comment['comment_content'] = $content_struct['content'];
    1310 
    1311                 do_action('xmlrpc_call', 'wp.newComment');
    1312 
    1313                 return wp_new_comment($comment);
    1314         }
    1315 
    1316         /**
    1317          * Retrieve all of the comment status.
    1318          *
    1319          * @since 2.7.0
    1320          *
    1321          * @param array $args Method parameters.
    1322          * @return array
    1323          */
    1324         function wp_getCommentStatusList($args) {
    1325                 $this->escape( $args );
    1326 
    1327                 $blog_id        = (int) $args[0];
    1328                 $username       = $args[1];
    1329                 $password       = $args[2];
    1330 
    1331                 if ( !$user = $this->login($username, $password) )
    1332                         return $this->error;
    1333 
    1334                 if ( !current_user_can( 'moderate_comments' ) )
    1335                         return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
    1336 
    1337                 do_action('xmlrpc_call', 'wp.getCommentStatusList');
    1338 
    1339                 return get_comment_statuses( );
    1340         }
    1341 
    1342         /**
    1343          * Retrieve comment count.
    1344          *
    1345          * @since 2.5.0
    1346          *
    1347          * @param array $args Method parameters.
    1348          * @return array
    1349          */
    1350         function wp_getCommentCount( $args ) {
    1351                 $this->escape($args);
    1352 
    1353                 $blog_id        = (int) $args[0];
    1354                 $username       = $args[1];
    1355                 $password       = $args[2];
    1356                 $post_id        = (int) $args[3];
    1357 
    1358                 if ( !$user = $this->login($username, $password) )
    1359                         return $this->error;
    1360 
    1361                 if ( !current_user_can( 'edit_posts' ) )
    1362                         return new IXR_Error( 403, __( 'You are not allowed access to details about comments.' ) );
    1363 
    1364                 do_action('xmlrpc_call', 'wp.getCommentCount');
    1365 
    1366                 $count = wp_count_comments( $post_id );
    1367                 return array(
    1368                         "approved" => $count->approved,
    1369                         "awaiting_moderation" => $count->moderated,
    1370                         "spam" => $count->spam,
    1371                         "total_comments" => $count->total_comments
    1372                 );
    1373         }
    1374 
    1375         /**
    1376          * Retrieve post statuses.
    1377          *
    1378          * @since 2.5.0
    1379          *
    1380          * @param array $args Method parameters.
    1381          * @return array
    1382          */
    1383         function wp_getPostStatusList( $args ) {
    1384                 $this->escape( $args );
    1385 
    1386                 $blog_id        = (int) $args[0];
    1387                 $username       = $args[1];
    1388                 $password       = $args[2];
    1389 
    1390                 if ( !$user = $this->login($username, $password) )
    1391                         return $this->error;
    1392 
    1393                 if ( !current_user_can( 'edit_posts' ) )
    1394                         return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
    1395 
    1396                 do_action('xmlrpc_call', 'wp.getPostStatusList');
    1397 
    1398                 return get_post_statuses( );
    1399         }
    1400 
    1401         /**
    1402          * Retrieve page statuses.
    1403          *
    1404          * @since 2.5.0
    1405          *
    1406          * @param array $args Method parameters.
    1407          * @return array
    1408          */
    1409         function wp_getPageStatusList( $args ) {
    1410                 $this->escape( $args );
    1411 
    1412                 $blog_id        = (int) $args[0];
    1413                 $username       = $args[1];
    1414                 $password       = $args[2];
    1415 
    1416                 if ( !$user = $this->login($username, $password) )
    1417                         return $this->error;
    1418 
    1419                 if ( !current_user_can( 'edit_posts' ) )
    1420                         return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
    1421 
    1422                 do_action('xmlrpc_call', 'wp.getPageStatusList');
    1423 
    1424                 return get_page_statuses( );
    1425         }
    1426 
    1427         /**
    1428          * Retrieve page templates.
    1429          *
    1430          * @since 2.6.0
    1431          *
    1432          * @param array $args Method parameters.
    1433          * @return array
    1434          */
    1435         function wp_getPageTemplates( $args ) {
    1436                 $this->escape( $args );
    1437 
    1438                 $blog_id        = (int) $args[0];
    1439                 $username       = $args[1];
    1440                 $password       = $args[2];
    1441 
    1442                 if ( !$user = $this->login($username, $password) )
    1443                         return $this->error;
    1444 
    1445                 if ( !current_user_can( 'edit_pages' ) )
    1446                         return new IXR_Error( 403, __( 'You are not allowed access to details about this site.' ) );
    1447 
    1448                 $templates = get_page_templates( );
    1449                 $templates['Default'] = 'default';
    1450 
    1451                 return $templates;
    1452         }
    1453 
    1454         /**
    1455          * Retrieve blog options.
    1456          *
    1457          * @since 2.6.0
    1458          *
    1459          * @param array $args Method parameters.
    1460          * @return array
    1461          */
    1462         function wp_getOptions( $args ) {
    1463                 $this->escape( $args );
    1464 
    1465                 $blog_id        = (int) $args[0];
    1466                 $username       = $args[1];
    1467                 $password       = $args[2];
    1468                 $options        = (array) $args[3];
    1469 
    1470                 if ( !$user = $this->login($username, $password) )
    1471                         return $this->error;
    1472 
    1473                 // If no specific options where asked for, return all of them
    1474                 if ( count( $options ) == 0 )
    1475                         $options = array_keys($this->blog_options);
    1476 
    1477                 return $this->_getOptions($options);
    1478         }
    1479 
    1480         /**
    1481          * Retrieve blog options value from list.
    1482          *
    1483          * @since 2.6.0
    1484          *
    1485          * @param array $options Options to retrieve.
    1486          * @return array
    1487          */
    1488         function _getOptions($options) {
    1489                 $data = array( );
    1490                 foreach ( $options as $option ) {
    1491                         if ( array_key_exists( $option, $this->blog_options ) ) {
    1492                                 $data[$option] = $this->blog_options[$option];
    1493                                 //Is the value static or dynamic?
    1494                                 if ( isset( $data[$option]['option'] ) ) {
    1495                                         $data[$option]['value'] = get_option( $data[$option]['option'] );
    1496                                         unset($data[$option]['option']);
    1497                                 }
    1498                         }
    1499                 }
    1500 
    1501                 return $data;
    1502         }
    1503 
    1504         /**
    1505          * Update blog options.
    1506          *
    1507          * @since 2.6.0
    1508          *
    1509          * @param array $args Method parameters.
    1510          * @return unknown
    1511          */
    1512         function wp_setOptions( $args ) {
    1513                 $this->escape( $args );
    1514 
    1515                 $blog_id        = (int) $args[0];
    1516                 $username       = $args[1];
    1517                 $password       = $args[2];
    1518                 $options        = (array) $args[3];
    1519 
    1520                 if ( !$user = $this->login($username, $password) )
    1521                         return $this->error;
    1522 
    1523                 if ( !current_user_can( 'manage_options' ) )
    1524                         return new IXR_Error( 403, __( 'You are not allowed to update options.' ) );
    1525 
    1526                 foreach ( $options as $o_name => $o_value ) {
    1527                         $option_names[] = $o_name;
    1528                         if ( !array_key_exists( $o_name, $this->blog_options ) )
    1529                                 continue;
    1530 
    1531                         if ( $this->blog_options[$o_name]['readonly'] == true )
    1532                                 continue;
    1533 
    1534                         update_option( $this->blog_options[$o_name]['option'], $o_value );
    1535                 }
    1536 
    1537                 //Now return the updated values
    1538                 return $this->_getOptions($option_names);
    1539         }
    1540 
    1541         /* Blogger API functions.
    1542          * specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/
    1543          */
    1544 
    1545         /**
    1546          * Retrieve blogs that user owns.
    1547          *
    1548          * Will make more sense once we support multiple blogs.
    1549          *
    1550          * @since 1.5.0
    1551          *
    1552          * @param array $args Method parameters.
    1553          * @return array
    1554          */
    1555         function blogger_getUsersBlogs($args) {
    1556                 if ( is_multisite() )
    1557                         return $this->_multisite_getUsersBlogs($args);
    1558 
    1559                 $this->escape($args);
    1560 
    1561                 $username = $args[1];
    1562                 $password  = $args[2];
    1563 
    1564                 if ( !$user = $this->login($username, $password) )
    1565                         return $this->error;
    1566 
    1567                 do_action('xmlrpc_call', 'blogger.getUsersBlogs');
    1568 
    1569                 $is_admin = current_user_can('manage_options');
    1570 
    1571                 $struct = array(
    1572                         'isAdmin'  => $is_admin,
    1573                         'url'      => get_option('home') . '/',
    1574                         'blogid'   => '1',
    1575                         'blogName' => get_option('blogname'),
    1576                         'xmlrpc'   => site_url( 'xmlrpc.php' )
    1577                 );
    1578 
    1579                 return array($struct);
    1580         }
    1581 
    1582         /**
    1583          * Private function for retrieving a users blogs for multisite setups
    1584          *
    1585          * @access protected
    1586          */
    1587         function _multisite_getUsersBlogs($args) {
    1588                 global $current_blog;
    1589                 $domain = $current_blog->domain;
    1590                 $path = $current_blog->path . 'xmlrpc.php';
    1591                 $protocol = is_ssl() ? 'https' : 'http';
    1592 
    1593                 $rpc = new IXR_Client("$protocol://{$domain}{$path}");
    1594                 $rpc->query('wp.getUsersBlogs', $args[1], $args[2]);
    1595                 $blogs = $rpc->getResponse();
    1596 
    1597                 if ( isset($blogs['faultCode']) )
    1598                         return new IXR_Error($blogs['faultCode'], $blogs['faultString']);
    1599 
    1600                 if ( $_SERVER['HTTP_HOST'] == $domain && $_SERVER['REQUEST_URI'] == $path ) {
    1601                         return $blogs;
    1602                 } else {
    1603                         foreach ( (array) $blogs as $blog ) {
    1604                                 if ( strpos($blog['url'], $_SERVER['HTTP_HOST']) )
    1605                                         return array($blog);
    1606                         }
    1607                         return array();
    1608                 }
    1609         }
    1610 
    1611         /**
    1612          * Retrieve user's data.
    1613          *
    1614          * Gives your client some info about you, so you don't have to.
    1615          *
    1616          * @since 1.5.0
    1617          *
    1618          * @param array $args Method parameters.
    1619          * @return array
    1620          */
    1621         function blogger_getUserInfo($args) {
    1622 
    1623                 $this->escape($args);
    1624 
    1625                 $username = $args[1];
    1626                 $password  = $args[2];
    1627 
    1628                 if ( !$user = $this->login($username, $password) )
    1629                         return $this->error;
    1630 
    1631                 if ( !current_user_can( 'edit_posts' ) )
    1632                         return new IXR_Error( 401, __( 'Sorry, you do not have access to user data on this site.' ) );
    1633 
    1634                 do_action('xmlrpc_call', 'blogger.getUserInfo');
    1635 
    1636                 $struct = array(
    1637                         'nickname'  => $user->nickname,
    1638                         'userid'    => $user->ID,
    1639                         'url'       => $user->user_url,
    1640                         'lastname'  => $user->last_name,
    1641                         'firstname' => $user->first_name
    1642                 );
    1643 
    1644                 return $struct;
    1645         }
    1646 
    1647         /**
    1648          * Retrieve post.
    1649          *
    1650          * @since 1.5.0
    1651          *
    1652          * @param array $args Method parameters.
    1653          * @return array
    1654          */
    1655         function blogger_getPost($args) {
    1656 
    1657                 $this->escape($args);
    1658 
    1659                 $post_ID    = (int) $args[1];
    1660                 $username = $args[2];
    1661                 $password  = $args[3];
    1662 
    1663                 if ( !$user = $this->login($username, $password) )
    1664                         return $this->error;
    1665 
    1666                 if ( !current_user_can( 'edit_post', $post_ID ) )
    1667                         return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
    1668 
    1669                 do_action('xmlrpc_call', 'blogger.getPost');
    1670 
    1671                 $post_data = wp_get_single_post($post_ID, ARRAY_A);
    1672 
    1673                 $categories = implode(',', wp_get_post_categories($post_ID));
    1674 
    1675                 $content  = '<title>'.stripslashes($post_data['post_title']).'</title>';
    1676                 $content .= '<category>'.$categories.'</category>';
    1677                 $content .= stripslashes($post_data['post_content']);
    1678 
    1679                 $struct = array(
    1680                         'userid'    => $post_data['post_author'],
    1681                         'dateCreated' => new IXR_Date(mysql2date('Ymd\TH:i:s', $post_data['post_date'], false)),
    1682                         'content'     => $content,
    1683                         'postid'  => $post_data['ID']
    1684                 );
    1685 
    1686                 return $struct;
    1687         }
    1688 
    1689         /**
    1690          * Retrieve list of recent posts.
    1691          *
    1692          * @since 1.5.0
    1693          *
    1694          * @param array $args Method parameters.
    1695          * @return array
    1696          */
    1697         function blogger_getRecentPosts($args) {
    1698 
    1699                 $this->escape($args);
    1700 
    1701                 $blog_ID    = (int) $args[1]; /* though we don't use it yet */
    1702                 $username = $args[2];
    1703                 $password  = $args[3];
    1704                 $num_posts  = $args[4];
    1705 
    1706                 if ( !$user = $this->login($username, $password) )
    1707                         return $this->error;
    1708 
    1709                 do_action('xmlrpc_call', 'blogger.getRecentPosts');
    1710 
    1711                 $posts_list = wp_get_recent_posts($num_posts);
    1712 
    1713                 if ( !$posts_list ) {
    1714                         $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
    1715                         return $this->error;
    1716                 }
    1717 
    1718                 foreach ($posts_list as $entry) {
    1719                         if ( !current_user_can( 'edit_post', $entry['ID'] ) )
    1720                                 continue;
    1721 
    1722                         $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false);
    1723                         $categories = implode(',', wp_get_post_categories($entry['ID']));
    1724 
    1725                         $content  = '<title>'.stripslashes($entry['post_title']).'</title>';
    1726                         $content .= '<category>'.$categories.'</category>';
    1727                         $content .= stripslashes($entry['post_content']);
    1728 
    1729                         $struct[] = array(
    1730                                 'userid' => $entry['post_author'],
    1731                                 'dateCreated' => new IXR_Date($post_date),
    1732                                 'content' => $content,
    1733                                 'postid' => $entry['ID'],
    1734                         );
    1735 
    1736                 }
    1737 
    1738                 $recent_posts = array();
    1739                 for ( $j=0; $j<count($struct); $j++ ) {
    1740                         array_push($recent_posts, $struct[$j]);
    1741                 }
    1742 
    1743                 return $recent_posts;
    1744         }
    1745 
    1746         /**
    1747          * Retrieve blog_filename content.
    1748          *
    1749          * @since 1.5.0
    1750          *
    1751          * @param array $args Method parameters.
    1752          * @return string
    1753          */
    1754         function blogger_getTemplate($args) {
    1755 
    1756                 $this->escape($args);
    1757 
    1758                 $blog_ID    = (int) $args[1];
    1759                 $username = $args[2];
    1760                 $password  = $args[3];
    1761                 $template   = $args[4]; /* could be 'main' or 'archiveIndex', but we don't use it */
    1762 
    1763                 if ( !$user = $this->login($username, $password) )
    1764                         return $this->error;
    1765 
    1766                 do_action('xmlrpc_call', 'blogger.getTemplate');
    1767 
    1768                 if ( !current_user_can('edit_themes') )
    1769                         return new IXR_Error(401, __('Sorry, this user can not edit the template.'));
    1770 
    1771                 /* warning: here we make the assumption that the blog's URL is on the same server */
    1772                 $filename = get_option('home') . '/';
    1773                 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);
    1774 
    1775                 $f = fopen($filename, 'r');
    1776                 $content = fread($f, filesize($filename));
    1777                 fclose($f);
    1778 
    1779                 /* so it is actually editable with a windows/mac client */
    1780                 // 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);
    1781 
    1782                 return $content;
    1783         }
    1784 
    1785         /**
    1786          * Updates the content of blog_filename.
    1787          *
    1788          * @since 1.5.0
    1789          *
    1790          * @param array $args Method parameters.
    1791          * @return bool True when done.
    1792          */
    1793         function blogger_setTemplate($args) {
    1794 
    1795                 $this->escape($args);
    1796 
    1797                 $blog_ID    = (int) $args[1];
    1798                 $username = $args[2];
    1799                 $password  = $args[3];
    1800                 $content    = $args[4];
    1801                 $template   = $args[5]; /* could be 'main' or 'archiveIndex', but we don't use it */
    1802 
    1803                 if ( !$user = $this->login($username, $password) )
    1804                         return $this->error;
    1805 
    1806                 do_action('xmlrpc_call', 'blogger.setTemplate');
    1807 
    1808                 if ( !current_user_can('edit_themes') )
    1809                         return new IXR_Error(401, __('Sorry, this user cannot edit the template.'));
    1810 
    1811                 /* warning: here we make the assumption that the blog's URL is on the same server */
    1812                 $filename = get_option('home') . '/';
    1813                 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);
    1814 
    1815                 if ($f = fopen($filename, 'w+')) {
    1816                         fwrite($f, $content);
    1817                         fclose($f);
    1818                 } else {
    1819                         return new IXR_Error(500, __('Either the file is not writable, or something wrong happened. The file has not been updated.'));
    1820                 }
    1821 
    1822                 return true;
    1823         }
    1824 
    1825         /**
    1826          * Create new post.
    1827          *
    1828          * @since 1.5.0
    1829          *
    1830          * @param array $args Method parameters.
    1831          * @return int
    1832          */
    1833         function blogger_newPost($args) {
    1834 
    1835                 $this->escape($args);
    1836 
    1837                 $blog_ID    = (int) $args[1]; /* though we don't use it yet */
    1838                 $username = $args[2];
    1839                 $password  = $args[3];
    1840                 $content    = $args[4];
    1841                 $publish    = $args[5];
    1842 
    1843                 if ( !$user = $this->login($username, $password) )
    1844                         return $this->error;
    1845 
    1846                 do_action('xmlrpc_call', 'blogger.newPost');
    1847 
    1848                 $cap = ($publish) ? 'publish_posts' : 'edit_posts';
    1849                 if ( !current_user_can($cap) )
    1850                         return new IXR_Error(401, __('Sorry, you are not allowed to post on this site.'));
    1851 
    1852                 $post_status = ($publish) ? 'publish' : 'draft';
    1853 
    1854                 $post_author = $user->ID;
    1855 
    1856                 $post_title = xmlrpc_getposttitle($content);
    1857                 $post_category = xmlrpc_getpostcategory($content);
    1858                 $post_content = xmlrpc_removepostdata($content);
    1859 
    1860                 $post_date = current_time('mysql');
    1861                 $post_date_gmt = current_time('mysql', 1);
    1862 
    1863                 $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status');
    1864 
    1865                 $post_ID = wp_insert_post($post_data);
    1866                 if ( is_wp_error( $post_ID ) )
    1867                         return new IXR_Error(500, $post_ID->get_error_message());
    1868 
    1869                 if ( !$post_ID )
    1870                         return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.'));
    1871 
    1872                 $this->attach_uploads( $post_ID, $post_content );
    1873 
    1874                 logIO('O', "Posted ! ID: $post_ID");
    1875 
    1876                 return $post_ID;
    1877         }
    1878 
    1879         /**
    1880          * Edit a post.
    1881          *
    1882          * @since 1.5.0
    1883          *
    1884          * @param array $args Method parameters.
    1885          * @return bool true when done.
    1886          */
    1887         function blogger_editPost($args) {
    1888 
    1889                 $this->escape($args);
    1890 
    1891                 $post_ID     = (int) $args[1];
    1892                 $username  = $args[2];
    1893                 $password   = $args[3];
    1894                 $content     = $args[4];
    1895                 $publish     = $args[5];
    1896 
    1897                 if ( !$user = $this->login($username, $password) )
    1898                         return $this->error;
    1899 
    1900                 do_action('xmlrpc_call', 'blogger.editPost');
    1901 
    1902                 $actual_post = wp_get_single_post($post_ID,ARRAY_A);
    1903 
    1904                 if ( !$actual_post || $actual_post['post_type'] != 'post' )
    1905                         return new IXR_Error(404, __('Sorry, no such post.'));
    1906 
    1907                 $this->escape($actual_post);
    1908 
    1909                 if ( !current_user_can('edit_post', $post_ID) )
    1910                         return new IXR_Error(401, __('Sorry, you do not have the right to edit this post.'));
    1911 
    1912                 extract($actual_post, EXTR_SKIP);
    1913 
    1914                 if ( ('publish' == $post_status) && !current_user_can('publish_posts') )
    1915                         return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.'));
    1916 
    1917                 $post_title = xmlrpc_getposttitle($content);
    1918                 $post_category = xmlrpc_getpostcategory($content);
    1919                 $post_content = xmlrpc_removepostdata($content);
    1920 
    1921                 $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt');
    1922 
    1923                 $result = wp_update_post($postdata);
    1924 
    1925                 if ( !$result )
    1926                         return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be edited.'));
    1927 
    1928                 $this->attach_uploads( $ID, $post_content );
    1929 
    1930                 return true;
    1931         }
    1932 
    1933         /**
    1934          * Remove a post.
    1935          *
    1936          * @since 1.5.0
    1937          *
    1938          * @param array $args Method parameters.
    1939          * @return bool True when post is deleted.
    1940          */
    1941         function blogger_deletePost($args) {
    1942                 $this->escape($args);
    1943 
    1944                 $post_ID     = (int) $args[1];
    1945                 $username  = $args[2];
    1946                 $password   = $args[3];
    1947                 $publish     = $args[4];
    1948 
    1949                 if ( !$user = $this->login($username, $password) )
    1950                         return $this->error;
    1951 
    1952                 do_action('xmlrpc_call', 'blogger.deletePost');
    1953 
    1954                 $actual_post = wp_get_single_post($post_ID,ARRAY_A);
    1955 
    1956                 if ( !$actual_post || $actual_post['post_type'] != 'post' )
    1957                         return new IXR_Error(404, __('Sorry, no such post.'));
    1958 
    1959                 if ( !current_user_can('edit_post', $post_ID) )
    1960                         return new IXR_Error(401, __('Sorry, you do not have the right to delete this post.'));
    1961 
    1962                 $result = wp_delete_post($post_ID);
    1963 
    1964                 if ( !$result )
    1965                         return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be deleted.'));
    1966 
    1967                 return true;
    1968         }
    1969 
    1970         /* MetaWeblog API functions
    1971          * specs on wherever Dave Winer wants them to be
    1972          */
    1973 
    1974         /**
    1975          * Create a new post.
    1976          *
    1977          * @since 1.5.0
    1978          *
    1979          * @param array $args Method parameters.
    1980          * @return int
    1981          */
    1982         function mw_newPost($args) {
    1983                 $this->escape($args);
    1984 
    1985                 $blog_ID     = (int) $args[0]; // we will support this in the near future
    1986                 $username  = $args[1];
    1987                 $password   = $args[2];
    1988                 $content_struct = $args[3];
    1989                 $publish     = $args[4];
    1990 
    1991                 if ( !$user = $this->login($username, $password) )
    1992                         return $this->error;
    1993 
    1994                 do_action('xmlrpc_call', 'metaWeblog.newPost');
    1995 
    1996                 $cap = ( $publish ) ? 'publish_posts' : 'edit_posts';
    1997                 $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
    1998                 $post_type = 'post';
    1999                 $page_template = '';
    2000                 if ( !empty( $content_struct['post_type'] ) ) {
    2001                         if ( $content_struct['post_type'] == 'page' ) {
    2002                                 $cap = ( $publish ) ? 'publish_pages' : 'edit_pages';
    2003                                 $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' );
    2004                                 $post_type = 'page';
    2005                                 if ( !empty( $content_struct['wp_page_template'] ) )
    2006                                         $page_template = $content_struct['wp_page_template'];
    2007                         } elseif ( $content_struct['post_type'] == 'post' ) {
    2008                                 // This is the default, no changes needed
    2009                         } else {
    2010                                 // No other post_type values are allowed here
    2011                                 return new IXR_Error( 401, __( 'Invalid post type.' ) );
    2012                         }
    2013                 }
    2014 
    2015                 if ( !current_user_can( $cap ) )
    2016                         return new IXR_Error( 401, $error_message );
    2017 
    2018                 // Let WordPress generate the post_name (slug) unless
    2019                 // one has been provided.
    2020                 $post_name = "";
    2021                 if ( isset($content_struct["wp_slug"]) )
    2022                         $post_name = $content_struct["wp_slug"];
    2023 
    2024                 // Only use a password if one was given.
    2025                 if ( isset($content_struct["wp_password"]) )
    2026                         $post_password = $content_struct["wp_password"];
    2027 
    2028                 // Only set a post parent if one was provided.
    2029                 if ( isset($content_struct["wp_page_parent_id"]) )
    2030                         $post_parent = $content_struct["wp_page_parent_id"];
    2031 
    2032                 // Only set the menu_order if it was provided.
    2033                 if ( isset($content_struct["wp_page_order"]) )
    2034                         $menu_order = $content_struct["wp_page_order"];
    2035 
    2036                 $post_author = $user->ID;
    2037 
    2038                 // If an author id was provided then use it instead.
    2039                 if ( isset($content_struct["wp_author_id"]) && ($user->ID != $content_struct["wp_author_id"]) ) {
    2040                         switch ( $post_type ) {
    2041                                 case "post":
    2042                                         if ( !current_user_can("edit_others_posts") )
    2043                                                 return(new IXR_Error(401, __("You are not allowed to post as this user")));
    2044                                         break;
    2045                                 case "page":
    2046                                         if ( !current_user_can("edit_others_pages") )
    2047                                                 return(new IXR_Error(401, __("You are not allowed to create pages as this user")));
    2048                                         break;
    2049                                 default:
    2050                                         return(new IXR_Error(401, __("Invalid post type.")));
    2051                                         break;
    2052                         }
    2053                         $post_author = $content_struct["wp_author_id"];
    2054                 }
    2055 
    2056                 $post_title = $content_struct['title'];
    2057                 $post_content = $content_struct['description'];
    2058 
    2059                 $post_status = $publish ? 'publish' : 'draft';
    2060 
    2061                 if ( isset( $content_struct["{$post_type}_status"] ) ) {
    2062                         switch ( $content_struct["{$post_type}_status"] ) {
    2063                                 case 'draft':
    2064                                 case 'private':
    2065                                 case 'publish':
    2066                                         $post_status = $content_struct["{$post_type}_status"];
    2067                                         break;
    2068                                 case 'pending':
    2069                                         // Pending is only valid for posts, not pages.
    2070                                         if ( $post_type === 'post' )
    2071                                                 $post_status = $content_struct["{$post_type}_status"];
    2072                                         break;
    2073                                 default:
    2074                                         $post_status = $publish ? 'publish' : 'draft';
    2075                                         break;
    2076                         }
    2077                 }
    2078 
    2079                 $post_excerpt = $content_struct['mt_excerpt'];
    2080                 $post_more = $content_struct['mt_text_more'];
    2081 
    2082                 $tags_input = $content_struct['mt_keywords'];
    2083 
    2084                 if ( isset($content_struct["mt_allow_comments"]) ) {
    2085                         if ( !is_numeric($content_struct["mt_allow_comments"]) ) {
    2086                                 switch ( $content_struct["mt_allow_comments"] ) {
    2087                                         case "closed":
    2088                                                 $comment_status = "closed";
    2089                                                 break;
    2090                                         case "open":
    2091                                                 $comment_status = "open";
    2092                                                 break;
    2093                                         default:
    2094                                                 $comment_status = get_option("default_comment_status");
    2095                                                 break;
    2096                                 }
    2097                         } else {
    2098                                 switch ( (int) $content_struct["mt_allow_comments"] ) {
    2099                                         case 0:
    2100                                         case 2:
    2101                                                 $comment_status = "closed";
    2102                                                 break;
    2103                                         case 1:
    2104                                                 $comment_status = "open";
    2105                                                 break;
    2106                                         default:
    2107                                                 $comment_status = get_option("default_comment_status");
    2108                                                 break;
    2109                                 }
    2110                         }
    2111                 } else {
    2112                         $comment_status = get_option("default_comment_status");
    2113                 }
    2114 
    2115                 if ( isset($content_struct["mt_allow_pings"]) ) {
    2116                         if ( !is_numeric($content_struct["mt_allow_pings"]) ) {
    2117                                 switch ( $content_struct['mt_allow_pings'] ) {
    2118                                         case "closed":
    2119                                                 $ping_status = "closed";
    2120                                                 break;
    2121                                         case "open":
    2122                                                 $ping_status = "open";
    2123                                                 break;
    2124                                         default:
    2125                                                 $ping_status = get_option("default_ping_status");
    2126                                                 break;
    2127                                 }
    2128                         } else {
    2129                                 switch ( (int) $content_struct["mt_allow_pings"] ) {
    2130                                         case 0:
    2131                                                 $ping_status = "closed";
    2132                                                 break;
    2133                                         case 1:
    2134                                                 $ping_status = "open";
    2135                                                 break;
    2136                                         default:
    2137                                                 $ping_status = get_option("default_ping_status");
    2138                                                 break;
    2139                                 }
    2140                         }
    2141                 } else {
    2142                         $ping_status = get_option("default_ping_status");
    2143                 }
    2144 
    2145                 if ( $post_more )
    2146                         $post_content = $post_content . "<!--more-->" . $post_more;
    2147 
    2148                 $to_ping = $content_struct['mt_tb_ping_urls'];
    2149                 if ( is_array($to_ping) )
    2150                         $to_ping = implode(' ', $to_ping);
    2151 
    2152                 // Do some timestamp voodoo
    2153                 if ( !empty( $content_struct['date_created_gmt'] ) )
    2154                         $dateCreated = str_replace( 'Z', '', $content_struct['date_created_gmt']->getIso() ) . 'Z'; // We know this is supposed to be GMT, so we're going to slap that Z on there by force
    2155                 elseif ( !empty( $content_struct['dateCreated']) )
    2156                         $dateCreated = $content_struct['dateCreated']->getIso();
    2157 
    2158                 if ( !empty( $dateCreated ) ) {
    2159                         $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
    2160                         $post_date_gmt = iso8601_to_datetime($dateCreated, GMT);
    2161                 } else {
    2162                         $post_date = current_time('mysql');
    2163                         $post_date_gmt = current_time('mysql', 1);
    2164                 }
    2165 
    2166                 $catnames = $content_struct['categories'];
    2167                 logIO('O', 'Post cats: ' . var_export($catnames,true));
    2168                 $post_category = array();
    2169 
    2170                 if ( is_array($catnames) ) {
    2171                         foreach ($catnames as $cat) {
    2172                                 $post_category[] = get_cat_ID($cat);
    2173                         }
    2174                 }
    2175 
    2176                 // We've got all the data -- post it:
    2177                 $postdata = compact('post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'to_ping', 'post_type', 'post_name', 'post_password', 'post_parent', 'menu_order', 'tags_input', 'page_template');
    2178 
    2179                 $post_ID = wp_insert_post($postdata, true);
    2180                 if ( is_wp_error( $post_ID ) )
    2181                         return new IXR_Error(500, $post_ID->get_error_message());
    2182 
    2183                 if ( !$post_ID )
    2184                         return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.'));
    2185 
    2186                 // Only posts can be sticky
    2187                 if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) {
    2188                         if ( $content_struct['sticky'] == true )
    2189                                 stick_post( $post_ID );
    2190                         elseif ( $content_struct['sticky'] == false )
    2191                                 unstick_post( $post_ID );
    2192                 }
    2193 
    2194                 if ( isset($content_struct['custom_fields']) )
    2195                         $this->set_custom_fields($post_ID, $content_struct['custom_fields']);
    2196 
    2197                 // Handle enclosures
    2198                 $this->add_enclosure_if_new($post_ID, $content_struct['enclosure']);
    2199 
    2200                 $this->attach_uploads( $post_ID, $post_content );
    2201 
    2202                 logIO('O', "Posted ! ID: $post_ID");
    2203 
    2204                 return strval($post_ID);
    2205         }
    2206 
    2207         function add_enclosure_if_new($post_ID, $enclosure) {
    2208                 if ( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) {
    2209 
    2210                         $encstring = $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type'];
    2211                         $found = false;
    2212                         foreach ( (array) get_post_custom($post_ID) as $key => $val) {
    2213                                 if ($key == 'enclosure') {
    2214                                         foreach ( (array) $val as $enc ) {
    2215                                                 if ($enc == $encstring) {
    2216                                                         $found = true;
    2217                                                         break 2;
    2218                                                 }
    2219                                         }
    2220                                 }
    2221                         }
    2222                         if (!$found)
    2223                                 add_post_meta( $post_ID, 'enclosure', $encstring );
    2224                 }
    2225         }
    2226 
    2227         /**
    2228          * Attach upload to a post.
    2229          *
    2230          * @since 2.1.0
    2231          *
    2232          * @param int $post_ID Post ID.
    2233          * @param string $post_content Post Content for attachment.
    2234          */
    2235         function attach_uploads( $post_ID, $post_content ) {
    2236                 global $wpdb;
    2237 
    2238                 // find any unattached files
    2239                 $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '0' AND post_type = 'attachment'" );
    2240                 if ( is_array( $attachments ) ) {
    2241                         foreach ( $attachments as $file ) {
    2242                                 if ( strpos( $post_content, $file->guid ) !== false )
    2243                                         $wpdb->update($wpdb->posts, array('post_parent' => $post_ID), array('ID' => $file->ID) );
    2244                         }
    2245                 }
    2246         }
    2247 
    2248         /**
    2249          * Edit a post.
    2250          *
    2251          * @since 1.5.0
    2252          *
    2253          * @param array $args Method parameters.
    2254          * @return bool True on success.
    2255          */
    2256         function mw_editPost($args) {
    2257 
    2258                 $this->escape($args);
    2259 
    2260                 $post_ID     = (int) $args[0];
    2261                 $username  = $args[1];
    2262                 $password   = $args[2];
    2263                 $content_struct = $args[3];
    2264                 $publish     = $args[4];
    2265 
    2266                 if ( !$user = $this->login($username, $password) )
    2267                         return $this->error;
    2268 
    2269                 do_action('xmlrpc_call', 'metaWeblog.editPost');
    2270 
    2271                 $cap = ( $publish ) ? 'publish_posts' : 'edit_posts';
    2272                 $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' );
    2273                 $post_type = 'post';
    2274                 $page_template = '';
    2275                 if ( !empty( $content_struct['post_type'] ) ) {
    2276                         if ( $content_struct['post_type'] == 'page' ) {
    2277                                 $cap = ( $publish ) ? 'publish_pages' : 'edit_pages';
    2278                                 $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' );
    2279                                 $post_type = 'page';
    2280                                 if ( !empty( $content_struct['wp_page_template'] ) )
    2281                                         $page_template = $content_struct['wp_page_template'];
    2282                         } elseif ( $content_struct['post_type'] == 'post' ) {
    2283                                 // This is the default, no changes needed
    2284                         } else {
    2285                                 // No other post_type values are allowed here
    2286                                 return new IXR_Error( 401, __( 'Invalid post type.' ) );
    2287                         }
    2288                 }
    2289 
    2290                 if ( !current_user_can( $cap ) )
    2291                         return new IXR_Error( 401, $error_message );
    2292 
    2293                 $postdata = wp_get_single_post($post_ID, ARRAY_A);
    2294 
    2295                 // If there is no post data for the give post id, stop
    2296                 // now and return an error.  Other wise a new post will be
    2297                 // created (which was the old behavior).
    2298                 if ( empty($postdata["ID"]) )
    2299                         return(new IXR_Error(404, __("Invalid post ID.")));
    2300 
    2301                 $this->escape($postdata);
    2302                 extract($postdata, EXTR_SKIP);
    2303 
    2304                 // Let WordPress manage slug if none was provided.
    2305                 $post_name = "";
    2306                 if ( isset($content_struct["wp_slug"]) )
    2307                         $post_name = $content_struct["wp_slug"];
    2308 
    2309                 // Only use a password if one was given.
    2310                 if ( isset($content_struct["wp_password"]) )
    2311                         $post_password = $content_struct["wp_password"];
    2312 
    2313                 // Only set a post parent if one was given.
    2314                 if ( isset($content_struct["wp_page_parent_id"]) )
    2315                         $post_parent = $content_struct["wp_page_parent_id"];
    2316 
    2317                 // Only set the menu_order if it was given.
    2318                 if ( isset($content_struct["wp_page_order"]) )
    2319                         $menu_order = $content_struct["wp_page_order"];
    2320 
    2321                 $post_author = $postdata["post_author"];
    2322 
    2323                 // Only set the post_author if one is set.
    2324                 if ( isset($content_struct["wp_author_id"]) && ($user->ID != $content_struct["wp_author_id"]) ) {
    2325                         switch ( $post_type ) {
    2326                                 case "post":
    2327                                         if ( !current_user_can("edit_others_posts") )
    2328                                                 return(new IXR_Error(401, __("You are not allowed to change the post author as this user.")));
    2329                                         break;
    2330                                 case "page":
    2331                                         if ( !current_user_can("edit_others_pages") )
    2332                                                 return(new IXR_Error(401, __("You are not allowed to change the page author as this user.")));
    2333                                         break;
    2334                                 default:
    2335                                         return(new IXR_Error(401, __("Invalid post type.")));
    2336                                         break;
    2337                         }
    2338                         $post_author = $content_struct["wp_author_id"];
    2339                 }
    2340 
    2341                 if ( isset($content_struct["mt_allow_comments"]) ) {
    2342                         if ( !is_numeric($content_struct["mt_allow_comments"]) ) {
    2343                                 switch ( $content_struct["mt_allow_comments"] ) {
    2344                                         case "closed":
    2345                                                 $comment_status = "closed";
    2346                                                 break;
    2347                                         case "open":
    2348                                                 $comment_status = "open";
    2349                                                 break;
    2350                                         default:
    2351                                                 $comment_status = get_option("default_comment_status");
    2352                                                 break;
    2353                                 }
    2354                         } else {
    2355                                 switch ( (int) $content_struct["mt_allow_comments"] ) {
    2356                                         case 0:
    2357                                         case 2:
    2358                                                 $comment_status = "closed";
    2359                                                 break;
    2360                                         case 1:
    2361                                                 $comment_status = "open";
    2362                                                 break;
    2363                                         default:
    2364                                                 $comment_status = get_option("default_comment_status");
    2365                                                 break;
    2366                                 }
    2367                         }
    2368                 }
    2369 
    2370                 if ( isset($content_struct["mt_allow_pings"]) ) {
    2371                         if ( !is_numeric($content_struct["mt_allow_pings"]) ) {
    2372                                 switch ( $content_struct["mt_allow_pings"] ) {
    2373                                         case "closed":
    2374                                                 $ping_status = "closed";
    2375                                                 break;
    2376                                         case "open":
    2377                                                 $ping_status = "open";
    2378                                                 break;
    2379                                         default:
    2380                                                 $ping_status = get_option("default_ping_status");
    2381                                                 break;
    2382                                 }
    2383                         } else {
    2384                                 switch ( (int) $content_struct["mt_allow_pings"] ) {
    2385                                         case 0:
    2386                                                 $ping_status = "closed";
    2387                                                 break;
    2388                                         case 1:
    2389                                                 $ping_status = "open";
    2390                                                 break;
    2391                                         default:
    2392                                                 $ping_status = get_option("default_ping_status");
    2393                                                 break;
    2394                                 }
    2395                         }
    2396                 }
    2397 
    2398                 $post_title = $content_struct['title'];
    2399                 $post_content = $content_struct['description'];
    2400                 $catnames = $content_struct['categories'];
    2401 
    2402                 $post_category = array();
    2403 
    2404                 if ( is_array($catnames) ) {
    2405                         foreach ($catnames as $cat) {
    2406                                 $post_category[] = get_cat_ID($cat);
    2407                         }
    2408                 }
    2409 
    2410                 $post_excerpt = $content_struct['mt_excerpt'];
    2411                 $post_more = $content_struct['mt_text_more'];
    2412 
    2413                 $post_status = $publish ? 'publish' : 'draft';
    2414                 if ( isset( $content_struct["{$post_type}_status"] ) ) {
    2415                         switch( $content_struct["{$post_type}_status"] ) {
    2416                                 case 'draft':
    2417                                 case 'private':
    2418                                 case 'publish':
    2419                                         $post_status = $content_struct["{$post_type}_status"];
    2420                                         break;
    2421                                 case 'pending':
    2422                                         // Pending is only valid for posts, not pages.
    2423                                         if ( $post_type === 'post' )
    2424                                                 $post_status = $content_struct["{$post_type}_status"];
    2425                                         break;
    2426                                 default:
    2427                                         $post_status = $publish ? 'publish' : 'draft';
    2428                                         break;
    2429                         }
    2430                 }
    2431 
    2432                 $tags_input = $content_struct['mt_keywords'];
    2433 
    2434                 if ( ('publish' == $post_status) ) {
    2435                         if ( ( 'page' == $post_type ) && !current_user_can('publish_pages') )
    2436                                 return new IXR_Error(401, __('Sorry, you do not have the right to publish this page.'));
    2437                         else if ( !current_user_can('publish_posts') )
    2438                                 return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.'));
    2439                 }
    2440 
    2441                 if ( $post_more )
    2442                         $post_content = $post_content . "<!--more-->" . $post_more;
    2443 
    2444                 $to_ping = $content_struct['mt_tb_ping_urls'];
    2445                 if ( is_array($to_ping) )
    2446                         $to_ping = implode(' ', $to_ping);
    2447 
    2448                 // Do some timestamp voodoo
    2449                 if ( !empty( $content_struct['date_created_gmt'] ) )
    2450                         $dateCreated = str_replace( 'Z', '', $content_struct['date_created_gmt']->getIso() ) . 'Z'; // We know this is supposed to be GMT, so we're going to slap that Z on there by force
    2451                 elseif ( !empty( $content_struct['dateCreated']) )
    2452                         $dateCreated = $content_struct['dateCreated']->getIso();
    2453 
    2454                 if ( !empty( $dateCreated ) ) {
    2455                         $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated));
    2456                         $post_date_gmt = iso8601_to_datetime($dateCreated, GMT);
    2457                 } else {
    2458                         $post_date     = $postdata['post_date'];
    2459                         $post_date_gmt = $postdata['post_date_gmt'];
    2460                 }
    2461 
    2462                 // We've got all the data -- post it:
    2463                 $newpost = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'post_date', 'post_date_gmt', 'to_ping', 'post_name', 'post_password', 'post_parent', 'menu_order', 'post_author', 'tags_input', 'page_template');
    2464 
    2465                 $result = wp_update_post($newpost, true);
    2466                 if ( is_wp_error( $result ) )
    2467                         return new IXR_Error(500, $result->get_error_message());
    2468 
    2469                 if ( !$result )
    2470                         return new IXR_Error(500, __('Sorry, your entry could not be edited. Something wrong happened.'));
    2471 
    2472                 // Only posts can be sticky
    2473                 if ( $post_type == 'post' && isset( $content_struct['sticky'] ) ) {
    2474                         if ( $content_struct['sticky'] == true )
    2475                                 stick_post( $post_ID );
    2476                         elseif ( $content_struct['sticky'] == false )
    2477                                 unstick_post( $post_ID );
    2478                 }
    2479 
    2480                 if ( isset($content_struct['custom_fields']) )
    2481                         $this->set_custom_fields($post_ID, $content_struct['custom_fields']);
    2482 
    2483                 // Handle enclosures
    2484                 $this->add_enclosure_if_new($post_ID, $content_struct['enclosure']);
    2485 
    2486                 $this->attach_uploads( $ID, $post_content );
    2487 
    2488                 logIO('O',"(MW) Edited ! ID: $post_ID");
    2489 
    2490                 return true;
    2491         }
    2492 
    2493         /**
    2494          * Retrieve post.
    2495          *
    2496          * @since 1.5.0
    2497          *
    2498          * @param array $args Method parameters.
    2499          * @return array
    2500          */
    2501         function mw_getPost($args) {
    2502 
    2503                 $this->escape($args);
    2504 
    2505                 $post_ID     = (int) $args[0];
    2506                 $username  = $args[1];
    2507                 $password   = $args[2];
    2508 
    2509                 if ( !$user = $this->login($username, $password) )
    2510                         return $this->error;
    2511 
    2512                 if ( !current_user_can( 'edit_post', $post_ID ) )
    2513                         return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
    2514 
    2515                 do_action('xmlrpc_call', 'metaWeblog.getPost');
    2516 
    2517                 $postdata = wp_get_single_post($post_ID, ARRAY_A);
    2518 
    2519                 if ($postdata['post_date'] != '') {
    2520                         $post_date = mysql2date('Ymd\TH:i:s', $postdata['post_date'], false);
    2521                         $post_date_gmt = mysql2date('Ymd\TH:i:s', $postdata['post_date_gmt'], false);
    2522 
    2523                         // For drafts use the GMT version of the post date
    2524                         if ( $postdata['post_status'] == 'draft' )
    2525                                 $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $postdata['post_date'] ), 'Ymd\TH:i:s' );
    2526 
    2527                         $categories = array();
    2528                         $catids = wp_get_post_categories($post_ID);
    2529                         foreach($catids as $catid)
    2530                                 $categories[] = get_cat_name($catid);
    2531 
    2532                         $tagnames = array();
    2533                         $tags = wp_get_post_tags( $post_ID );
    2534                         if ( !empty( $tags ) ) {
    2535                                 foreach ( $tags as $tag )
    2536                                         $tagnames[] = $tag->name;
    2537                                 $tagnames = implode( ', ', $tagnames );
    2538                         } else {
    2539                                 $tagnames = '';
    2540                         }
    2541 
    2542                         $post = get_extended($postdata['post_content']);
    2543                         $link = post_permalink($postdata['ID']);
    2544 
    2545                         // Get the author info.
    2546                         $author = get_userdata($postdata['post_author']);
    2547 
    2548                         $allow_comments = ('open' == $postdata['comment_status']) ? 1 : 0;
    2549                         $allow_pings = ('open' == $postdata['ping_status']) ? 1 : 0;
    2550 
    2551                         // Consider future posts as published
    2552                         if ( $postdata['post_status'] === 'future' )
    2553                                 $postdata['post_status'] = 'publish';
    2554 
    2555                         $sticky = false;
    2556                         if ( is_sticky( $post_ID ) )
    2557                                 $sticky = true;
    2558 
    2559                         $enclosure = array();
    2560                         foreach ( (array) get_post_custom($post_ID) as $key => $val) {
    2561                                 if ($key == 'enclosure') {
    2562                                         foreach ( (array) $val as $enc ) {
    2563                                                 $encdata = split("\n", $enc);
    2564                                                 $enclosure['url'] = trim(htmlspecialchars($encdata[0]));
    2565                                                 $enclosure['length'] = (int) trim($encdata[1]);
    2566                                                 $enclosure['type'] = trim($encdata[2]);
    2567                                                 break 2;
    2568                                         }
    2569                                 }
    2570                         }
    2571 
    2572                         $resp = array(
    2573                                 'dateCreated' => new IXR_Date($post_date),
    2574                                 'userid' => $postdata['post_author'],
    2575                                 'postid' => $postdata['ID'],
    2576                                 'description' => $post['main'],
    2577                                 'title' => $postdata['post_title'],
    2578                                 'link' => $link,
    2579                                 'permaLink' => $link,
    2580                                 // commented out because no other tool seems to use this
    2581                                 //            'content' => $entry['post_content'],
    2582                                 'categories' => $categories,
    2583                                 'mt_excerpt' => $postdata['post_excerpt'],
    2584                                 'mt_text_more' => $post['extended'],
    2585                                 'mt_allow_comments' => $allow_comments,
    2586                                 'mt_allow_pings' => $allow_pings,
    2587                                 'mt_keywords' => $tagnames,
    2588                                 'wp_slug' => $postdata['post_name'],
    2589                                 'wp_password' => $postdata['post_password'],
    2590                                 'wp_author_id' => $author->ID,
    2591                                 'wp_author_display_name'        => $author->display_name,
    2592                                 'date_created_gmt' => new IXR_Date($post_date_gmt),
    2593                                 'post_status' => $postdata['post_status'],
    2594                                 'custom_fields' => $this->get_custom_fields($post_ID),
    2595                                 'sticky' => $sticky
    2596                         );
    2597 
    2598                         if ( !empty($enclosure) ) $resp['enclosure'] = $enclosure;
    2599 
    2600                         return $resp;
    2601                 } else {
    2602                         return new IXR_Error(404, __('Sorry, no such post.'));
    2603                 }
    2604         }
    2605 
    2606         /**
    2607          * Retrieve list of recent posts.
    2608          *
    2609          * @since 1.5.0
    2610          *
    2611          * @param array $args Method parameters.
    2612          * @return array
    2613          */
    2614         function mw_getRecentPosts($args) {
    2615 
    2616                 $this->escape($args);
    2617 
    2618                 $blog_ID     = (int) $args[0];
    2619                 $username  = $args[1];
    2620                 $password   = $args[2];
    2621                 $num_posts   = (int) $args[3];
    2622 
    2623                 if ( !$user = $this->login($username, $password) )
    2624                         return $this->error;
    2625 
    2626                 do_action('xmlrpc_call', 'metaWeblog.getRecentPosts');
    2627 
    2628                 $posts_list = wp_get_recent_posts($num_posts);
    2629 
    2630                 if ( !$posts_list )
    2631                         return array( );
    2632 
    2633                 foreach ($posts_list as $entry) {
    2634                         if ( !current_user_can( 'edit_post', $entry['ID'] ) )
    2635                                 continue;
    2636 
    2637                         $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false);
    2638                         $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt'], false);
    2639 
    2640                         // For drafts use the GMT version of the date
    2641                         if ( $entry['post_status'] == 'draft' )
    2642                                 $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $entry['post_date'] ), 'Ymd\TH:i:s' );
    2643 
    2644                         $categories = array();
    2645                         $catids = wp_get_post_categories($entry['ID']);
    2646                         foreach( $catids as $catid )
    2647                                 $categories[] = get_cat_name($catid);
    2648 
    2649                         $tagnames = array();
    2650                         $tags = wp_get_post_tags( $entry['ID'] );
    2651                         if ( !empty( $tags ) ) {
    2652                                 foreach ( $tags as $tag ) {
    2653                                         $tagnames[] = $tag->name;
    2654                                 }
    2655                                 $tagnames = implode( ', ', $tagnames );
    2656                         } else {
    2657                                 $tagnames = '';
    2658                         }
    2659 
    2660                         $post = get_extended($entry['post_content']);
    2661                         $link = post_permalink($entry['ID']);
    2662 
    2663                         // Get the post author info.
    2664                         $author = get_userdata($entry['post_author']);
    2665 
    2666                         $allow_comments = ('open' == $entry['comment_status']) ? 1 : 0;
    2667                         $allow_pings = ('open' == $entry['ping_status']) ? 1 : 0;
    2668 
    2669                         // Consider future posts as published
    2670                         if ( $entry['post_status'] === 'future' )
    2671                                 $entry['post_status'] = 'publish';
    2672 
    2673                         $struct[] = array(
    2674                                 'dateCreated' => new IXR_Date($post_date),
    2675                                 'userid' => $entry['post_author'],
    2676                                 'postid' => $entry['ID'],
    2677                                 'description' => $post['main'],
    2678                                 'title' => $entry['post_title'],
    2679                                 'link' => $link,
    2680                                 'permaLink' => $link,
    2681                                 // commented out because no other tool seems to use this
    2682                                 // 'content' => $entry['post_content'],
    2683                                 'categories' => $categories,
    2684                                 'mt_excerpt' => $entry['post_excerpt'],
    2685                                 'mt_text_more' => $post['extended'],
    2686                                 'mt_allow_comments' => $allow_comments,
    2687                                 'mt_allow_pings' => $allow_pings,
    2688                                 'mt_keywords' => $tagnames,
    2689                                 'wp_slug' => $entry['post_name'],
    2690                                 'wp_password' => $entry['post_password'],
    2691                                 'wp_author_id' => $author->ID,
    2692                                 'wp_author_display_name' => $author->display_name,
    2693                                 'date_created_gmt' => new IXR_Date($post_date_gmt),
    2694                                 'post_status' => $entry['post_status'],
    2695                                 'custom_fields' => $this->get_custom_fields($entry['ID'])
    2696                         );
    2697 
    2698                 }
    2699 
    2700                 $recent_posts = array();
    2701                 for ( $j=0; $j<count($struct); $j++ ) {
    2702                         array_push($recent_posts, $struct[$j]);
    2703                 }
    2704 
    2705                 return $recent_posts;
    2706         }
    2707 
    2708         /**
    2709          * Retrieve the list of categories on a given blog.
    2710          *
    2711          * @since 1.5.0
    2712          *
    2713          * @param array $args Method parameters.
    2714          * @return array
    2715          */
    2716         function mw_getCategories($args) {
    2717 
    2718                 $this->escape($args);
    2719 
    2720                 $blog_ID     = (int) $args[0];
    2721                 $username  = $args[1];
    2722                 $password   = $args[2];
    2723 
    2724                 if ( !$user = $this->login($username, $password) )
    2725                         return $this->error;
    2726 
    2727                 if ( !current_user_can( 'edit_posts' ) )
    2728                         return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );
    2729 
    2730                 do_action('xmlrpc_call', 'metaWeblog.getCategories');
    2731 
    2732                 $categories_struct = array();
    2733 
    2734                 if ( $cats = get_categories(array('get' => 'all')) ) {
    2735                         foreach ( $cats as $cat ) {
    2736                                 $struct['categoryId'] = $cat->term_id;
    2737                                 $struct['parentId'] = $cat->parent;
    2738                                 $struct['description'] = $cat->name;
    2739                                 $struct['categoryDescription'] = $cat->description;
    2740                                 $struct['categoryName'] = $cat->name;
    2741                                 $struct['htmlUrl'] = esc_html(get_category_link($cat->term_id));
    2742                                 $struct['rssUrl'] = esc_html(get_category_feed_link($cat->term_id, 'rss2'));
    2743 
    2744                                 $categories_struct[] = $struct;
    2745                         }
    2746                 }
    2747 
    2748                 return $categories_struct;
    2749         }
    2750 
    2751         /**
    2752          * Uploads a file, following your settings.
    2753          *
    2754          * Adapted from a patch by Johann Richard.
    2755          *
    2756          * @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/
    2757          *
    2758          * @since 1.5.0
    2759          *
    2760          * @param array $args Method parameters.
    2761          * @return array
    2762          */
    2763         function mw_newMediaObject($args) {
    2764                 global $wpdb;
    2765 
    2766                 $blog_ID     = (int) $args[0];
    2767                 $username  = $wpdb->escape($args[1]);
    2768                 $password   = $wpdb->escape($args[2]);
    2769                 $data        = $args[3];
    2770 
    2771                 $name = sanitize_file_name( $data['name'] );
    2772                 $type = $data['type'];
    2773                 $bits = $data['bits'];
    2774 
    2775                 logIO('O', '(MW) Received '.strlen($bits).' bytes');
    2776 
    2777                 if ( !$user = $this->login($username, $password) )
    2778                         return $this->error;
    2779 
    2780                 do_action('xmlrpc_call', 'metaWeblog.newMediaObject');
    2781 
    2782                 if ( !current_user_can('upload_files') ) {
    2783                         logIO('O', '(MW) User does not have upload_files capability');
    2784                         $this->error = new IXR_Error(401, __('You are not allowed to upload files to this site.'));
    2785                         return $this->error;
    2786                 }
    2787 
    2788                 if ( $upload_err = apply_filters( "pre_upload_error", false ) )
    2789                         return new IXR_Error(500, $upload_err);
    2790 
    2791                 if ( !empty($data["overwrite"]) && ($data["overwrite"] == true) ) {
    2792                         // Get postmeta info on the object.
    2793                         $old_file = $wpdb->get_row("
    2794                                 SELECT ID
    2795                                 FROM {$wpdb->posts}
    2796                                 WHERE post_title = '{$name}'
    2797                                         AND post_type = 'attachment'
    2798                         ");
    2799 
    2800                         // Delete previous file.
    2801                         wp_delete_attachment($old_file->ID);
    2802 
    2803                         // Make sure the new name is different by pre-pending the
    2804                         // previous post id.
    2805                         $filename = preg_replace("/^wpid\d+-/", "", $name);
    2806                         $name = "wpid{$old_file->ID}-{$filename}";
    2807                 }
    2808 
    2809                 $upload = wp_upload_bits($name, $type, $bits);
    2810                 if ( ! empty($upload['error']) ) {
    2811                         $errorString = sprintf(__('Could not write file %1$s (%2$s)'), $name, $upload['error']);
    2812                         logIO('O', '(MW) ' . $errorString);
    2813                         return new IXR_Error(500, $errorString);
    2814                 }
    2815                 // Construct the attachment array
    2816                 // attach to post_id 0
    2817                 $post_id = 0;
    2818                 $attachment = array(
    2819                         'post_title' => $name,
    2820                         'post_content' => '',
    2821                         'post_type' => 'attachment',
    2822                         'post_parent' => $post_id,
    2823                         'post_mime_type' => $type,
    2824                         'guid' => $upload[ 'url' ]
    2825                 );
    2826 
    2827                 // Save the data
    2828                 $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id );
    2829                 wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );
    2830 
    2831                 return apply_filters( 'wp_handle_upload', array( 'file' => $name, 'url' => $upload[ 'url' ], 'type' => $type ), 'upload' );
    2832         }
    2833 
    2834         /* MovableType API functions
    2835          * specs on http://www.movabletype.org/docs/mtmanual_programmatic.html
    2836          */
    2837 
    2838         /**
    2839          * Retrieve the post titles of recent posts.
    2840          *
    2841          * @since 1.5.0
    2842          *
    2843          * @param array $args Method parameters.
    2844          * @return array
    2845          */
    2846         function mt_getRecentPostTitles($args) {
    2847 
    2848                 $this->escape($args);
    2849 
    2850                 $blog_ID     = (int) $args[0];
    2851                 $username  = $args[1];
    2852                 $password   = $args[2];
    2853                 $num_posts   = (int) $args[3];
    2854 
    2855                 if ( !$user = $this->login($username, $password) )
    2856                         return $this->error;
    2857 
    2858                 do_action('xmlrpc_call', 'mt.getRecentPostTitles');
    2859 
    2860                 $posts_list = wp_get_recent_posts($num_posts);
    2861 
    2862                 if ( !$posts_list ) {
    2863                         $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.'));
    2864                         return $this->error;
    2865                 }
    2866 
    2867                 foreach ($posts_list as $entry) {
    2868                         if ( !current_user_can( 'edit_post', $entry['ID'] ) )
    2869                                 continue;
    2870 
    2871                         $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date'], false);
    2872                         $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt'], false);
    2873 
    2874                         // For drafts use the GMT version of the date
    2875                         if ( $entry['post_status'] == 'draft' )
    2876                                 $post_date_gmt = get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $entry['post_date'] ), 'Ymd\TH:i:s' );
    2877 
    2878                         $struct[] = array(
    2879                                 'dateCreated' => new IXR_Date($post_date),
    2880                                 'userid' => $entry['post_author'],
    2881                                 'postid' => $entry['ID'],
    2882                                 'title' => $entry['post_title'],
    2883                                 'date_created_gmt' => new IXR_Date($post_date_gmt)
    2884                         );
    2885 
    2886                 }
    2887 
    2888                 $recent_posts = array();
    2889                 for ( $j=0; $j<count($struct); $j++ ) {
    2890                         array_push($recent_posts, $struct[$j]);
    2891                 }
    2892 
    2893                 return $recent_posts;
    2894         }
    2895 
    2896         /**
    2897          * Retrieve list of all categories on blog.
    2898          *
    2899          * @since 1.5.0
    2900          *
    2901          * @param array $args Method parameters.
    2902          * @return array
    2903          */
    2904         function mt_getCategoryList($args) {
    2905 
    2906                 $this->escape($args);
    2907 
    2908                 $blog_ID     = (int) $args[0];
    2909                 $username  = $args[1];
    2910                 $password   = $args[2];
    2911 
    2912                 if ( !$user = $this->login($username, $password) )
    2913                         return $this->error;
    2914 
    2915                 if ( !current_user_can( 'edit_posts' ) )
    2916                         return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) );
    2917 
    2918                 do_action('xmlrpc_call', 'mt.getCategoryList');
    2919 
    2920                 $categories_struct = array();
    2921 
    2922                 if ( $cats = get_categories(array('hide_empty' => 0, 'hierarchical' => 0)) ) {
    2923                         foreach ( $cats as $cat ) {
    2924                                 $struct['categoryId'] = $cat->term_id;
    2925                                 $struct['categoryName'] = $cat->name;
    2926 
    2927                                 $categories_struct[] = $struct;
    2928                         }
    2929                 }
    2930 
    2931                 return $categories_struct;
    2932         }
    2933 
    2934         /**
    2935          * Retrieve post categories.
    2936          *
    2937          * @since 1.5.0
    2938          *
    2939          * @param array $args Method parameters.
    2940          * @return array
    2941          */
    2942         function mt_getPostCategories($args) {
    2943 
    2944                 $this->escape($args);
    2945 
    2946                 $post_ID     = (int) $args[0];
    2947                 $username  = $args[1];
    2948                 $password   = $args[2];
    2949 
    2950                 if ( !$user = $this->login($username, $password) )
    2951                         return $this->error;
    2952 
    2953                 if ( !current_user_can( 'edit_post', $post_ID ) )
    2954                         return new IXR_Error( 401, __( 'Sorry, you can not edit this post.' ) );
    2955 
    2956                 do_action('xmlrpc_call', 'mt.getPostCategories');
    2957 
    2958                 $categories = array();
    2959                 $catids = wp_get_post_categories(intval($post_ID));
    2960                 // first listed category will be the primary category
    2961                 $isPrimary = true;
    2962                 foreach ( $catids as $catid ) {
    2963                         $categories[] = array(
    2964                                 'categoryName' => get_cat_name($catid),
    2965                                 'categoryId' => (string) $catid,
    2966                                 'isPrimary' => $isPrimary
    2967                         );
    2968                         $isPrimary = false;
    2969                 }
    2970 
    2971                 return $categories;
    2972         }
    2973 
    2974         /**
    2975          * Sets categories for a post.
    2976          *
    2977          * @since 1.5.0
    2978          *
    2979          * @param array $args Method parameters.
    2980          * @return bool True on success.
    2981          */
    2982         function mt_setPostCategories($args) {
    2983 
    2984                 $this->escape($args);
    2985 
    2986                 $post_ID     = (int) $args[0];
    2987                 $username  = $args[1];
    2988                 $password   = $args[2];
    2989                 $categories  = $args[3];
    2990 
    2991                 if ( !$user = $this->login($username, $password) )
    2992                         return $this->error;
    2993 
    2994                 do_action('xmlrpc_call', 'mt.setPostCategories');
    2995 
    2996                 if ( !current_user_can('edit_post', $post_ID) )
    2997                         return new IXR_Error(401, __('Sorry, you cannot edit this post.'));
    2998 
    2999                 foreach ( $categories as $cat ) {
    3000                         $catids[] = $cat['categoryId'];
    3001                 }
    3002 
    3003                 wp_set_post_categories($post_ID, $catids);
    3004 
    3005                 return true;
    3006         }
    3007 
    3008         /**
    3009          * Retrieve an array of methods supported by this server.
    3010          *
    3011          * @since 1.5.0
    3012          *
    3013          * @param array $args Method parameters.
    3014          * @return array
    3015          */
    3016         function mt_supportedMethods($args) {
    3017 
    3018                 do_action('xmlrpc_call', 'mt.supportedMethods');
    3019 
    3020                 $supported_methods = array();
    3021                 foreach ( $this->methods as $key => $value ) {
    3022                         $supported_methods[] = $key;
    3023                 }
    3024 
    3025                 return $supported_methods;
    3026         }
    3027 
    3028         /**
    3029          * Retrieve an empty array because we don't support per-post text filters.
    3030          *
    3031          * @since 1.5.0
    3032          *
    3033          * @param array $args Method parameters.
    3034          */
    3035         function mt_supportedTextFilters($args) {
    3036                 do_action('xmlrpc_call', 'mt.supportedTextFilters');
    3037                 return apply_filters('xmlrpc_text_filters', array());
    3038         }
    3039 
    3040         /**
    3041          * Retrieve trackbacks sent to a given post.
    3042          *
    3043          * @since 1.5.0
    3044          *
    3045          * @param array $args Method parameters.
    3046          * @return mixed
    3047          */
    3048         function mt_getTrackbackPings($args) {
    3049 
    3050                 global $wpdb;
    3051 
    3052                 $post_ID = intval($args);
    3053 
    3054                 do_action('xmlrpc_call', 'mt.getTrackbackPings');
    3055 
    3056                 $actual_post = wp_get_single_post($post_ID, ARRAY_A);
    3057 
    3058                 if ( !$actual_post )
    3059                         return new IXR_Error(404, __('Sorry, no such post.'));
    3060 
    3061                 $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_ID) );
    3062 
    3063                 if ( !$comments )
    3064                         return array();
    3065 
    3066                 $trackback_pings = array();
    3067                 foreach ( $comments as $comment ) {
    3068                         if ( 'trackback' == $comment->comment_type ) {
    3069                                 $content = $comment->comment_content;
    3070                                 $title = substr($content, 8, (strpos($content, '</strong>') - 8));
    3071                                 $trackback_pings[] = array(
    3072                                         'pingTitle' => $title,
    3073                                         'pingURL'   => $comment->comment_author_url,
    3074                                         'pingIP'    => $comment->comment_author_IP
    3075                                 );
    3076                         }
    3077                 }
    3078 
    3079                 return $trackback_pings;
    3080         }
    3081 
    3082         /**
    3083          * Sets a post's publish status to 'publish'.
    3084          *
    3085          * @since 1.5.0
    3086          *
    3087          * @param array $args Method parameters.
    3088          * @return int
    3089          */
    3090         function mt_publishPost($args) {
    3091 
    3092                 $this->escape($args);
    3093 
    3094                 $post_ID     = (int) $args[0];
    3095                 $username  = $args[1];
    3096                 $password   = $args[2];
    3097 
    3098                 if ( !$user = $this->login($username, $password) )
    3099                         return $this->error;
    3100 
    3101                 do_action('xmlrpc_call', 'mt.publishPost');
    3102 
    3103                 if ( !current_user_can('edit_post', $post_ID) )
    3104                         return new IXR_Error(401, __('Sorry, you cannot edit this post.'));
    3105 
    3106                 $postdata = wp_get_single_post($post_ID,ARRAY_A);
    3107 
    3108                 $postdata['post_status'] = 'publish';
    3109 
    3110                 // retain old cats
    3111                 $cats = wp_get_post_categories($post_ID);
    3112                 $postdata['post_category'] = $cats;
    3113                 $this->escape($postdata);
    3114 
    3115                 $result = wp_update_post($postdata);
    3116 
    3117                 return $result;
    3118         }
    3119 
    3120         /* PingBack functions
    3121          * specs on www.hixie.ch/specs/pingback/pingback
    3122          */
    3123 
    3124         /**
    3125          * Retrieves a pingback and registers it.
    3126          *
    3127          * @since 1.5.0
    3128          *
    3129          * @param array $args Method parameters.
    3130          * @return array
    3131          */
    3132         function pingback_ping($args) {
    3133                 global $wpdb;
    3134 
    3135                 do_action('xmlrpc_call', 'pingback.ping');
    3136 
    3137                 $this->escape($args);
    3138 
    3139                 $pagelinkedfrom = $args[0];
    3140                 $pagelinkedto   = $args[1];
    3141 
    3142                 $title = '';
    3143 
    3144                 $pagelinkedfrom = str_replace('&amp;', '&', $pagelinkedfrom);
    3145                 $pagelinkedto = str_replace('&amp;', '&', $pagelinkedto);
    3146                 $pagelinkedto = str_replace('&', '&amp;', $pagelinkedto);
    3147 
    3148                 // Check if the page linked to is in our site
    3149                 $pos1 = strpos($pagelinkedto, str_replace(array('http://www.','http://','https://www.','https://'), '', get_option('home')));
    3150                 if ( !$pos1 )
    3151                         return new IXR_Error(0, __('Is there no link to us?'));
    3152 
    3153                 // let's find which post is linked to
    3154                 // FIXME: does url_to_postid() cover all these cases already?
    3155                 //        if so, then let's use it and drop the old code.
    3156                 $urltest = parse_url($pagelinkedto);
    3157                 if ( $post_ID = url_to_postid($pagelinkedto) ) {
    3158                         $way = 'url_to_postid()';
    3159                 } elseif ( preg_match('#p/[0-9]{1,}#', $urltest['path'], $match) ) {
    3160                         // the path defines the post_ID (archives/p/XXXX)
    3161                         $blah = explode('/', $match[0]);
    3162                         $post_ID = (int) $blah[1];
    3163                         $way = 'from the path';
    3164                 } elseif ( preg_match('#p=[0-9]{1,}#', $urltest['query'], $match) ) {
    3165                         // the querystring defines the post_ID (?p=XXXX)
    3166                         $blah = explode('=', $match[0]);
    3167                         $post_ID = (int) $blah[1];
    3168                         $way = 'from the querystring';
    3169                 } elseif ( isset($urltest['fragment']) ) {
    3170                         // an #anchor is there, it's either...
    3171                         if ( intval($urltest['fragment']) ) {
    3172                                 // ...an integer #XXXX (simpliest case)
    3173                                 $post_ID = (int) $urltest['fragment'];
    3174                                 $way = 'from the fragment (numeric)';
    3175                         } elseif ( preg_match('/post-[0-9]+/',$urltest['fragment']) ) {
    3176                                 // ...a post id in the form 'post-###'
    3177                                 $post_ID = preg_replace('/[^0-9]+/', '', $urltest['fragment']);
    3178                                 $way = 'from the fragment (post-###)';
    3179                         } elseif ( is_string($urltest['fragment']) ) {
    3180                                 // ...or a string #title, a little more complicated
    3181                                 $title = preg_replace('/[^a-z0-9]/i', '.', $urltest['fragment']);
    3182                                 $sql = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", $title);
    3183                                 if (! ($post_ID = $wpdb->get_var($sql)) ) {
    3184                                         // returning unknown error '0' is better than die()ing
    3185                                         return new IXR_Error(0, '');
    3186                                 }
    3187                                 $way = 'from the fragment (title)';
    3188                         }
    3189                 } else {
    3190                         // TODO: Attempt to extract a post ID from the given URL
    3191                         return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.'));
    3192                 }
    3193                 $post_ID = (int) $post_ID;
    3194 
    3195 
    3196                 logIO("O","(PB) URL='$pagelinkedto' ID='$post_ID' Found='$way'");
    3197 
    3198                 $post = get_post($post_ID);
    3199 
    3200                 if ( !$post ) // Post_ID not found
    3201                         return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.'));
    3202 
    3203                 if ( $post_ID == url_to_postid($pagelinkedfrom) )
    3204                         return new IXR_Error(0, __('The source URL and the target URL cannot both point to the same resource.'));
    3205 
    3206                 // Check if pings are on
    3207                 if ( !pings_open($post) )
    3208                         return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.'));
    3209 
    3210                 // Let's check that the remote site didn't already pingback this entry
    3211                 if ( $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_ID, $pagelinkedfrom) ) )
    3212                         return new IXR_Error( 48, __( 'The pingback has already been registered.' ) );
    3213 
    3214                 // very stupid, but gives time to the 'from' server to publish !
    3215                 sleep(1);
    3216 
    3217                 // Let's check the remote site
    3218                 $linea = wp_remote_fopen( $pagelinkedfrom );
    3219                 if ( !$linea )
    3220                         return new IXR_Error(16, __('The source URL does not exist.'));
    3221 
    3222                 $linea = apply_filters('pre_remote_source', $linea, $pagelinkedto);
    3223 
    3224                 // Work around bug in strip_tags():
    3225                 $linea = str_replace('<!DOC', '<DOC', $linea);
    3226                 $linea = preg_replace( '/[\s\r\n\t]+/', ' ', $linea ); // normalize spaces
    3227                 $linea = preg_replace( "/ <(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $linea );
    3228 
    3229                 preg_match('|<title>([^<]*?)</title>|is', $linea, $matchtitle);
    3230                 $title = $matchtitle[1];
    3231                 if ( empty( $title ) )
    3232                         return new IXR_Error(32, __('We cannot find a title on that page.'));
    3233 
    3234                 $linea = strip_tags( $linea, '<a>' ); // just keep the tag we need
    3235 
    3236                 $p = explode( "\n\n", $linea );
    3237 
    3238                 $preg_target = preg_quote($pagelinkedto, '|');
    3239 
    3240                 foreach ( $p as $para ) {
    3241                         if ( strpos($para, $pagelinkedto) !== false ) { // it exists, but is it a link?
    3242                                 preg_match("|<a[^>]+?".$preg_target."[^>]*>([^>]+?)</a>|", $para, $context);
    3243 
    3244                                 // If the URL isn't in a link context, keep looking
    3245                                 if ( empty($context) )
    3246                                         continue;
    3247 
    3248                                 // We're going to use this fake tag to mark the context in a bit
    3249                                 // the marker is needed in case the link text appears more than once in the paragraph
    3250                                 $excerpt = preg_replace('|\</?wpcontext\>|', '', $para);
    3251 
    3252                                 // prevent really long link text
    3253                                 if ( strlen($context[1]) > 100 )
    3254                                         $context[1] = substr($context[1], 0, 100) . '...';
    3255 
    3256                                 $marker = '<wpcontext>'.$context[1].'</wpcontext>';    // set up our marker
    3257                                 $excerpt= str_replace($context[0], $marker, $excerpt); // swap out the link for our marker
    3258                                 $excerpt = strip_tags($excerpt, '<wpcontext>');        // strip all tags but our context marker
    3259                                 $excerpt = trim($excerpt);
    3260                                 $preg_marker = preg_quote($marker, '|');
    3261                                 $excerpt = preg_replace("|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt);
    3262                                 $excerpt = strip_tags($excerpt); // YES, again, to remove the marker wrapper
    3263                                 break;
    3264                         }
    3265                 }
    3266 
    3267                 if ( empty($context) ) // Link to target not found
    3268                         return new IXR_Error(17, __('The source URL does not contain a link to the target URL, and so cannot be used as a source.'));
    3269 
    3270                 $pagelinkedfrom = str_replace('&', '&amp;', $pagelinkedfrom);
    3271 
    3272                 $context = '[...] ' . esc_html( $excerpt ) . ' [...]';
    3273                 $pagelinkedfrom = $wpdb->escape( $pagelinkedfrom );
    3274 
    3275                 $comment_post_ID = (int) $post_ID;
    3276                 $comment_author = $title;
    3277                 $this->escape($comment_author);
    3278                 $comment_author_url = $pagelinkedfrom;
    3279                 $comment_content = $context;
    3280                 $this->escape($comment_content);
    3281                 $comment_type = 'pingback';
    3282 
    3283                 $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_content', 'comment_type');
    3284 
    3285                 $comment_ID = wp_new_comment($commentdata);
    3286                 do_action('pingback_post', $comment_ID);
    3287 
    3288                 return sprintf(__('Pingback from %1$s to %2$s registered. Keep the web talking! :-)'), $pagelinkedfrom, $pagelinkedto);
    3289         }
    3290 
    3291         /**
    3292          * Retrieve array of URLs that pingbacked the given URL.
    3293          *
    3294          * Specs on http://www.aquarionics.com/misc/archives/blogite/0198.html
    3295          *
    3296          * @since 1.5.0
    3297          *
    3298          * @param array $args Method parameters.
    3299          * @return array
    3300          */
    3301         function pingback_extensions_getPingbacks($args) {
    3302 
    3303                 global $wpdb;
    3304 
    3305                 do_action('xmlrpc_call', 'pingback.extensions.getPingbacks');
    3306 
    3307                 $this->escape($args);
    3308 
    3309                 $url = $args;
    3310 
    3311                 $post_ID = url_to_postid($url);
    3312                 if ( !$post_ID ) {
    3313                         // We aren't sure that the resource is available and/or pingback enabled
    3314                         return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn&#8217;t exist, or it is not a pingback-enabled resource.'));
    3315                 }
    3316 
    3317                 $actual_post = wp_get_single_post($post_ID, ARRAY_A);
    3318 
    3319                 if ( !$actual_post ) {
    3320                         // No such post = resource not found
    3321                         return new IXR_Error(32, __('The specified target URL does not exist.'));
    3322                 }
    3323 
    3324                 $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_ID) );
    3325 
    3326                 if ( !$comments )
    3327                         return array();
    3328 
    3329                 $pingbacks = array();
    3330                 foreach ( $comments as $comment ) {
    3331                         if ( 'pingback' == $comment->comment_type )
    3332                                 $pingbacks[] = $comment->comment_author_url;
    3333                 }
    3334 
    3335                 return $pingbacks;
    3336         }
    3337 }
    3338 
    333978$wp_xmlrpc_server = new wp_xmlrpc_server();
    334079$wp_xmlrpc_server->serve_request();
    334180?>