web/wp-includes/class-wp-xmlrpc-server.php
changeset 204 09a1c134465b
parent 194 32102edaa81b
--- a/web/wp-includes/class-wp-xmlrpc-server.php	Wed Dec 19 12:35:13 2012 -0800
+++ b/web/wp-includes/class-wp-xmlrpc-server.php	Wed Dec 19 17:46:52 2012 -0800
@@ -48,6 +48,10 @@
 			'wp.getTerms'			=> 'this:wp_getTerms',
 			'wp.getTaxonomy'		=> 'this:wp_getTaxonomy',
 			'wp.getTaxonomies'		=> 'this:wp_getTaxonomies',
+			'wp.getUser'			=> 'this:wp_getUser',
+			'wp.getUsers'			=> 'this:wp_getUsers',
+			'wp.getProfile'			=> 'this:wp_getProfile',
+			'wp.editProfile'		=> 'this:wp_editProfile',
 			'wp.getPage'			=> 'this:wp_getPage',
 			'wp.getPages'			=> 'this:wp_getPages',
 			'wp.newPage'			=> 'this:wp_newPage',
@@ -78,14 +82,14 @@
 			'wp.getPostFormats'     => 'this:wp_getPostFormats',
 			'wp.getPostType'		=> 'this:wp_getPostType',
 			'wp.getPostTypes'		=> 'this:wp_getPostTypes',
+			'wp.getRevisions'		=> 'this:wp_getRevisions',
+			'wp.restoreRevision'	=> 'this:wp_restoreRevision',
 
 			// Blogger API
 			'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs',
 			'blogger.getUserInfo' => 'this:blogger_getUserInfo',
 			'blogger.getPost' => 'this:blogger_getPost',
 			'blogger.getRecentPosts' => 'this:blogger_getRecentPosts',
-			'blogger.getTemplate' => 'this:blogger_getTemplate',
-			'blogger.setTemplate' => 'this:blogger_setTemplate',
 			'blogger.newPost' => 'this:blogger_newPost',
 			'blogger.editPost' => 'this:blogger_editPost',
 			'blogger.deletePost' => 'this:blogger_deletePost',
@@ -101,8 +105,6 @@
 			// MetaWeblog API aliases for Blogger API
 			// see http://www.xmlrpc.com/stories/storyReader$2460
 			'metaWeblog.deletePost' => 'this:blogger_deletePost',
-			'metaWeblog.getTemplate' => 'this:blogger_getTemplate',
-			'metaWeblog.setTemplate' => 'this:blogger_setTemplate',
 			'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs',
 
 			// MovableType API
@@ -158,48 +160,33 @@
 	}
 
 	/**
-	 * Check user's credentials.
-	 *
-	 * @since 1.5.0
-	 *
-	 * @param string $user_login User's username.
-	 * @param string $user_pass User's password.
-	 * @return bool Whether authentication passed.
-	 * @deprecated use wp_xmlrpc_server::login
-	 * @see wp_xmlrpc_server::login
-	 */
-	function login_pass_ok($user_login, $user_pass) {
-		if ( !get_option( 'enable_xmlrpc' ) ) {
-			$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') ) );
-			return false;
-		}
-
-		if (!user_pass_ok($user_login, $user_pass)) {
-			$this->error = new IXR_Error(403, __('Bad login/pass combination.'));
-			return false;
-		}
-		return true;
-	}
-
-	/**
 	 * Log user in.
 	 *
-	 * @since 2.8
+	 * @since 2.8.0
 	 *
 	 * @param string $username User's username.
 	 * @param string $password User's password.
 	 * @return mixed WP_User object if authentication passed, false otherwise
 	 */
-	function login($username, $password) {
-		if ( !get_option( 'enable_xmlrpc' ) ) {
-			$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') ) );
+	function login( $username, $password ) {
+		// Respect any old filters against get_option() for 'enable_xmlrpc'.
+		$enabled = apply_filters( 'pre_option_enable_xmlrpc', false ); // Deprecated
+		if ( false === $enabled )
+			$enabled = apply_filters( 'option_enable_xmlrpc', true ); // Deprecated
+
+		// Proper filter for turning off XML-RPC. It is on by default.
+		$enabled = apply_filters( 'xmlrpc_enabled', $enabled );
+
+		if ( ! $enabled ) {
+			$this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site.' ) ) );
 			return false;
 		}
 
 		$user = wp_authenticate($username, $password);
 
 		if (is_wp_error($user)) {
-			$this->error = new IXR_Error(403, __('Bad login/pass combination.'));
+			$this->error = new IXR_Error( 403, __( 'Incorrect username or password.' ) );
+			$this->error = apply_filters( 'xmlrpc_login_error', $this->error, $user );
 			return false;
 		}
 
@@ -208,6 +195,22 @@
 	}
 
 	/**
+	 * Check user's credentials. Deprecated.
+	 *
+	 * @since 1.5.0
+	 * @deprecated 2.8.0
+	 * @deprecated use wp_xmlrpc_server::login
+	 * @see wp_xmlrpc_server::login
+	 *
+	 * @param string $username User's username.
+	 * @param string $password User's password.
+	 * @return bool Whether authentication passed.
+	 */
+	function login_pass_ok( $username, $password ) {
+		return (bool) $this->login( $username, $password );
+	}
+
+	/**
 	 * Sanitize string or array of strings for database.
 	 *
 	 * @since 1.5.2
@@ -319,6 +322,11 @@
 				'readonly'      => true,
 				'option'        => 'siteurl'
 			),
+			'home_url'          => array(
+				'desc'          => __( 'Home URL' ),
+				'readonly'      => true,
+				'option'        => 'home'
+			),
 			'image_default_link_type' => array(
 				'desc'          => __( 'Image default link type' ),
 				'readonly'      => true,
@@ -473,15 +481,17 @@
 				continue;
 
 			$blog_id = $blog->userblog_id;
-			switch_to_blog($blog_id);
-			$is_admin = current_user_can('manage_options');
+
+			switch_to_blog( $blog_id );
+
+			$is_admin = current_user_can( 'manage_options' );
 
 			$struct[] = array(
 				'isAdmin'		=> $is_admin,
-				'url'			=> get_option( 'home' ) . '/',
+				'url'			=> home_url( '/' ),
 				'blogid'		=> (string) $blog_id,
 				'blogName'		=> get_option( 'blogname' ),
-				'xmlrpc'		=> site_url( 'xmlrpc.php' )
+				'xmlrpc'		=> site_url( 'xmlrpc.php', 'rpc' ),
 			);
 
 			restore_current_blog();
@@ -773,7 +783,7 @@
 		// Get info the page parent if there is one.
 		$parent_title = "";
 		if ( ! empty( $page->post_parent ) ) {
-			$parent = get_page( $page->post_parent );
+			$parent = get_post( $page->post_parent );
 			$parent_title = $parent->post_title;
 		}
 
@@ -871,6 +881,46 @@
 	}
 
 	/**
+	 * Prepares user data for return in an XML-RPC object.
+	 *
+	 * @access protected
+	 *
+	 * @param WP_User $user The unprepared user object
+	 * @param array $fields The subset of user fields to return
+	 * @return array The prepared user data
+	 */
+	protected function _prepare_user( $user, $fields ) {
+		$_user = array( 'user_id' => strval( $user->ID ) );
+
+		$user_fields = array(
+			'username'          => $user->user_login,
+			'first_name'        => $user->user_firstname,
+			'last_name'         => $user->user_lastname,
+			'registered'        => $this->_convert_date( $user->user_registered ),
+			'bio'               => $user->user_description,
+			'email'             => $user->user_email,
+			'nickname'          => $user->nickname,
+			'nicename'          => $user->user_nicename,
+			'url'               => $user->user_url,
+			'display_name'      => $user->display_name,
+			'roles'             => $user->roles,
+		);
+
+		if ( in_array( 'all', $fields ) ) {
+			$_user = array_merge( $_user, $user_fields );
+		} else {
+			if ( in_array( 'basic', $fields ) ) {
+				$basic_fields = array( 'username', 'email', 'registered', 'display_name', 'nicename' );
+				$fields = array_merge( $fields, $basic_fields );
+			}
+			$requested_fields = array_intersect_key( $user_fields, array_flip( $fields ) );
+			$_user = array_merge( $_user, $requested_fields );
+		}
+
+		return apply_filters( 'xmlrpc_prepare_user', $_user, $user, $fields );
+	}
+
+	/**
 	 * Create a new post for any registered post type.
 	 *
 	 * @since 3.4.0
@@ -885,7 +935,7 @@
 	 *      - post_status (default: 'draft')
 	 *      - post_title
 	 *      - post_author
-	 *      - post_exerpt
+	 *      - post_excerpt
 	 *      - post_content
 	 *      - post_date_gmt | post_date
 	 *      - post_format
@@ -962,7 +1012,7 @@
 			if ( $post_data['post_type'] != get_post_type( $post_data['ID'] ) )
 				return new IXR_Error( 401, __( 'The post type may not be changed.' ) );
 		} else {
-			if ( ! current_user_can( $post_type->cap->edit_posts ) )
+			if ( ! current_user_can( $post_type->cap->create_posts ) || ! current_user_can( $post_type->cap->edit_posts ) )
 				return new IXR_Error( 401, __( 'Sorry, you are not allowed to post on this site.' ) );
 		}
 
@@ -980,7 +1030,8 @@
 					return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts in this post type' ) );
 				break;
 			default:
-				$post_data['post_status'] = 'draft';
+				if ( ! get_post_status_object( $post_data['post_status'] ) )
+					$post_data['post_status'] = 'draft';
 			break;
 		}
 
@@ -1045,8 +1096,9 @@
 			// empty value deletes, non-empty value adds/updates
 			if ( ! $post_data['post_thumbnail'] )
 				delete_post_thumbnail( $post_ID );
-			elseif ( ! set_post_thumbnail( $post_ID, $post_data['post_thumbnail'] ) )
-					return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
+			elseif ( ! get_post( absint( $post_data['post_thumbnail'] ) ) )
+				return new IXR_Error( 404, __( 'Invalid attachment ID.' ) );
+			set_post_thumbnail( $post_ID, $post_data['post_thumbnail'] );
 			unset( $content_struct['post_thumbnail'] );
 		}
 
@@ -1157,7 +1209,7 @@
 
 		$post_data = apply_filters( 'xmlrpc_wp_insert_post_data', $post_data, $content_struct );
 
-		$post_ID = wp_insert_post( $post_data, true );
+		$post_ID = $update ? wp_update_post( $post_data, true ) : wp_insert_post( $post_data, true );
 		if ( is_wp_error( $post_ID ) )
 			return new IXR_Error( 500, $post_ID->get_error_message() );
 
@@ -1205,6 +1257,13 @@
 		if ( empty( $post['ID'] ) )
 			return new IXR_Error( 404, __( 'Invalid post ID.' ) );
 
+		if ( isset( $content_struct['if_not_modified_since'] ) ) {
+			// If the post has been modified since the date provided, return an error.
+			if ( mysql2date( 'U', $post['post_modified_gmt'] ) > $content_struct['if_not_modified_since']->getTimestamp() ) {
+				return new IXR_Error( 409, __( 'There is a revision of this post that is more recent.' ) );
+			}
+		}
+
 		// convert the date field back to IXR form
 		$post['post_date'] = $this->_convert_date( $post['post_date'] );
 
@@ -1254,7 +1313,7 @@
 
 		do_action( 'xmlrpc_call', 'wp.deletePost' );
 
-		$post = wp_get_single_post( $post_id, ARRAY_A );
+		$post = get_post( $post_id, ARRAY_A );
 		if ( empty( $post['ID'] ) )
 			return new IXR_Error( 404, __( 'Invalid post ID.' ) );
 
@@ -1284,7 +1343,7 @@
 	 * groups are 'post' (all basic fields), 'taxonomies', 'custom_fields',
 	 * and 'enclosure'.
 	 *
-	 * @uses wp_get_single_post()
+	 * @uses get_post()
 	 * @param array $args Method parameters. Contains:
 	 *  - int     $post_id
 	 *  - string  $username
@@ -1335,7 +1394,7 @@
 
 		do_action( 'xmlrpc_call', 'wp.getPost' );
 
-		$post = wp_get_single_post( $post_id, ARRAY_A );
+		$post = get_post( $post_id, ARRAY_A );
 
 		if ( empty( $post['ID'] ) )
 			return new IXR_Error( 404, __( 'Invalid post ID.' ) );
@@ -1423,6 +1482,10 @@
 				$query['order'] = $filter['order'];
 		}
 
+		if ( isset( $filter['s'] ) ) {
+			$query['s'] = $filter['s'];
+		}
+
 		$posts_list = wp_get_recent_posts( $query );
 
 		if ( ! $posts_list )
@@ -1918,6 +1981,262 @@
 	}
 
 	/**
+	 * Retrieve a user.
+	 *
+	 * The optional $fields parameter specifies what fields will be included
+	 * in the response array. This should be a list of field names. 'user_id' will
+	 * always be included in the response regardless of the value of $fields.
+	 *
+	 * Instead of, or in addition to, individual field names, conceptual group
+	 * names can be used to specify multiple fields. The available conceptual
+	 * groups are 'basic' and 'all'.
+	 *
+	 * @uses get_userdata()
+	 * @param array $args Method parameters. Contains:
+	 *  - int     $blog_id
+	 *  - string  $username
+	 *  - string  $password
+	 *  - int     $user_id
+	 *  - array   $fields optional
+	 * @return array contains (based on $fields parameter):
+	 *  - 'user_id'
+	 *  - 'username'
+	 *  - 'first_name'
+	 *  - 'last_name'
+	 *  - 'registered'
+	 *  - 'bio'
+	 *  - 'email'
+	 *  - 'nickname'
+	 *  - 'nicename'
+	 *  - 'url'
+	 *  - 'display_name'
+	 *  - 'roles'
+	 */
+	function wp_getUser( $args ) {
+		if ( ! $this->minimum_args( $args, 4 ) )
+			return $this->error;
+
+		$this->escape( $args );
+
+		$blog_id    = (int) $args[0];
+		$username   = $args[1];
+		$password   = $args[2];
+		$user_id    = (int) $args[3];
+
+		if ( isset( $args[4] ) )
+			$fields = $args[4];
+		else
+			$fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUser' );
+
+		if ( ! $user = $this->login( $username, $password ) )
+			return $this->error;
+
+		do_action( 'xmlrpc_call', 'wp.getUser' );
+
+		if ( ! current_user_can( 'edit_user', $user_id ) )
+			return new IXR_Error( 401, __( 'Sorry, you cannot edit users.' ) );
+
+		$user_data = get_userdata( $user_id );
+
+		if ( ! $user_data )
+			return new IXR_Error( 404, __( 'Invalid user ID' ) );
+
+		return $this->_prepare_user( $user_data, $fields );
+	}
+
+	/**
+	 * Retrieve users.
+	 *
+	 * The optional $filter parameter modifies the query used to retrieve users.
+	 * Accepted keys are 'number' (default: 50), 'offset' (default: 0), 'role',
+	 * 'who', 'orderby', and 'order'.
+	 *
+	 * The optional $fields parameter specifies what fields will be included
+	 * in the response array.
+	 *
+	 * @uses get_users()
+	 * @see wp_getUser() for more on $fields and return values
+	 *
+	 * @param array $args Method parameters. Contains:
+	 *  - int     $blog_id
+	 *  - string  $username
+	 *  - string  $password
+	 *  - array   $filter optional
+	 *  - array   $fields optional
+	 * @return array users data
+	 */
+	function wp_getUsers( $args ) {
+		if ( ! $this->minimum_args( $args, 3 ) )
+			return $this->error;
+
+		$this->escape( $args );
+
+		$blog_id    = (int) $args[0];
+		$username   = $args[1];
+		$password   = $args[2];
+		$filter     = isset( $args[3] ) ? $args[3] : array();
+
+		if ( isset( $args[4] ) )
+			$fields = $args[4];
+		else
+			$fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUsers' );
+
+		if ( ! $user = $this->login( $username, $password ) )
+			return $this->error;
+
+		do_action( 'xmlrpc_call', 'wp.getUsers' );
+
+		if ( ! current_user_can( 'list_users' ) )
+			return new IXR_Error( 401, __( 'Sorry, you cannot list users.' ) );
+
+		$query = array( 'fields' => 'all_with_meta' );
+
+		$query['number'] = ( isset( $filter['number'] ) ) ? absint( $filter['number'] ) : 50;
+		$query['offset'] = ( isset( $filter['offset'] ) ) ? absint( $filter['offset'] ) : 0;
+
+		if ( isset( $filter['orderby'] ) ) {
+			$query['orderby'] = $filter['orderby'];
+
+			if ( isset( $filter['order'] ) )
+				$query['order'] = $filter['order'];
+		}
+
+		if ( isset( $filter['role'] ) ) {
+			if ( get_role( $filter['role'] ) === null )
+				return new IXR_Error( 403, __( 'The role specified is not valid' ) );
+
+			$query['role'] = $filter['role'];
+		}
+
+		if ( isset( $filter['who'] ) ) {
+			$query['who'] = $filter['who'];
+		}
+
+		$users = get_users( $query );
+
+		$_users = array();
+		foreach ( $users as $user_data ) {
+			if ( current_user_can( 'edit_user', $user_data->ID ) )
+				$_users[] = $this->_prepare_user( $user_data, $fields );
+		}
+		return $_users;
+	}
+
+	/**
+	 * Retrieve information about the requesting user.
+	 *
+	 * @uses get_userdata()
+	 * @param array $args Method parameters. Contains:
+	 *  - int     $blog_id
+	 *  - string  $username
+	 *  - string  $password
+	 *  - array   $fields optional
+	 * @return array (@see wp_getUser)
+	 */
+	function wp_getProfile( $args ) {
+		if ( ! $this->minimum_args( $args, 3 ) )
+			return $this->error;
+
+		$this->escape( $args );
+
+		$blog_id    = (int) $args[0];
+		$username   = $args[1];
+		$password   = $args[2];
+
+		if ( isset( $args[3] ) )
+			$fields = $args[3];
+		else
+			$fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getProfile' );
+
+		if ( ! $user = $this->login( $username, $password ) )
+			return $this->error;
+
+		do_action( 'xmlrpc_call', 'wp.getProfile' );
+
+		if ( ! current_user_can( 'edit_user', $user->ID ) )
+			return new IXR_Error( 401, __( 'Sorry, you cannot edit your profile.' ) );
+
+		$user_data = get_userdata( $user->ID );
+
+		return $this->_prepare_user( $user_data, $fields );
+	}
+
+	/**
+	 * Edit user's profile.
+	 *
+	 * @uses wp_update_user()
+	 * @param array $args Method parameters. Contains:
+	 *  - int     $blog_id
+	 *  - string  $username
+	 *  - string  $password
+	 *  - array   $content_struct
+	 *      It can optionally contain:
+	 *      - 'first_name'
+	 *      - 'last_name'
+	 *      - 'website'
+	 *      - 'display_name'
+	 *      - 'nickname'
+	 *      - 'nicename'
+	 *      - 'bio'
+	 * @return bool True, on success.
+	 */
+	function wp_editProfile( $args ) {
+		if ( ! $this->minimum_args( $args, 4 ) )
+			return $this->error;
+
+		$this->escape( $args );
+
+		$blog_id        = (int) $args[0];
+		$username       = $args[1];
+		$password       = $args[2];
+		$content_struct = $args[3];
+
+		if ( ! $user = $this->login( $username, $password ) )
+			return $this->error;
+
+		do_action( 'xmlrpc_call', 'wp.editProfile' );
+
+		if ( ! current_user_can( 'edit_user', $user->ID ) )
+			return new IXR_Error( 401, __( 'Sorry, you cannot edit your profile.' ) );
+
+		// holds data of the user
+		$user_data = array();
+		$user_data['ID'] = $user->ID;
+
+		// only set the user details if it was given
+		if ( isset( $content_struct['first_name'] ) )
+			$user_data['first_name'] = $content_struct['first_name'];
+
+		if ( isset( $content_struct['last_name'] ) )
+			$user_data['last_name'] = $content_struct['last_name'];
+
+		if ( isset( $content_struct['url'] ) )
+			$user_data['user_url'] = $content_struct['url'];
+
+		if ( isset( $content_struct['display_name'] ) )
+			$user_data['display_name'] = $content_struct['display_name'];
+
+		if ( isset( $content_struct['nickname'] ) )
+			$user_data['nickname'] = $content_struct['nickname'];
+
+		if ( isset( $content_struct['nicename'] ) )
+			$user_data['user_nicename'] = $content_struct['nicename'];
+
+		if ( isset( $content_struct['bio'] ) )
+			$user_data['description'] = $content_struct['bio'];
+
+		$result = wp_update_user( $user_data );
+
+		if ( is_wp_error( $result ) )
+			return new IXR_Error( 500, $result->get_error_message() );
+
+		if ( ! $result )
+			return new IXR_Error( 500, __( 'Sorry, the user cannot be updated.' ) );
+
+		return true;
+	}
+
+	/**
 	 * Retrieve page.
 	 *
 	 * @since 2.2.0
@@ -1941,7 +2260,7 @@
 			return $this->error;
 		}
 
-		$page = get_page($page_id);
+		$page = get_post($page_id);
 		if ( ! $page )
 			return new IXR_Error( 404, __( 'Invalid post ID.' ) );
 
@@ -2058,7 +2377,7 @@
 
 		// Get the current page based on the page_id and
 		// make sure it is a page and not a post.
-		$actual_page = wp_get_single_post($page_id, ARRAY_A);
+		$actual_page = get_post($page_id, ARRAY_A);
 		if ( !$actual_page || ($actual_page['post_type'] != 'page') )
 			return(new IXR_Error(404, __('Sorry, no such page.')));
 
@@ -2099,7 +2418,7 @@
 		do_action('xmlrpc_call', 'wp.editPage');
 
 		// Get the page data and make sure it is a page.
-		$actual_page = wp_get_single_post($page_id, ARRAY_A);
+		$actual_page = get_post($page_id, ARRAY_A);
 		if ( !$actual_page || ($actual_page['post_type'] != 'page') )
 			return(new IXR_Error(404, __('Sorry, no such page.')));
 
@@ -3169,6 +3488,128 @@
 		return $struct;
 	}
 
+	/**
+	 * Retrieve revisions for a specific post.
+	 *
+	 * @since 3.5.0
+	 *
+	 * The optional $fields parameter specifies what fields will be included
+	 * in the response array.
+	 *
+	 * @uses wp_get_post_revisions()
+	 * @see wp_getPost() for more on $fields
+	 *
+	 * @param array $args Method parameters. Contains:
+	 *  - int     $blog_id
+	 *  - string  $username
+	 *  - string  $password
+	 *  - int     $post_id
+	 *  - array   $fields
+	 * @return array contains a collection of posts.
+	 */
+	function wp_getRevisions( $args ) {
+		if ( ! $this->minimum_args( $args, 4 ) )
+			return $this->error;
+
+		$this->escape( $args );
+
+		$blog_id    = (int) $args[0];
+		$username   = $args[1];
+		$password   = $args[2];
+		$post_id    = (int) $args[3];
+
+		if ( isset( $args[4] ) )
+			$fields = $args[4];
+		else
+			$fields = apply_filters( 'xmlrpc_default_revision_fields', array( 'post_date', 'post_date_gmt' ), 'wp.getRevisions' );
+
+		if ( ! $user = $this->login( $username, $password ) )
+			return $this->error;
+
+		do_action( 'xmlrpc_call', 'wp.getRevisions' );
+
+		if ( ! $post = get_post( $post_id ) )
+			return new IXR_Error( 404, __( 'Invalid post ID' ) );
+
+		if ( ! current_user_can( 'edit_post', $post_id ) )
+			return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) );
+
+		// Check if revisions are enabled.
+		if ( ! WP_POST_REVISIONS || ! post_type_supports( $post->post_type, 'revisions' ) )
+			return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) );
+
+		$revisions = wp_get_post_revisions( $post_id );
+
+		if ( ! $revisions )
+			return array();
+
+		$struct = array();
+
+		foreach ( $revisions as $revision ) {
+			if ( ! current_user_can( 'read_post', $revision->ID ) )
+				continue;
+
+			// Skip autosaves
+			if ( wp_is_post_autosave( $revision ) )
+				continue;
+
+			$struct[] = $this->_prepare_post( get_object_vars( $revision ), $fields );
+		}
+
+		return $struct;
+	}
+
+	/**
+	 * Restore a post revision
+	 *
+	 * @since 3.5.0
+	 *
+	 * @uses wp_restore_post_revision()
+	 *
+	 * @param array $args Method parameters. Contains:
+	 *  - int     $blog_id
+	 *  - string  $username
+	 *  - string  $password
+	 *  - int     $post_id
+	 * @return bool false if there was an error restoring, true if success.
+	 */
+	function wp_restoreRevision( $args ) {
+		if ( ! $this->minimum_args( $args, 3 ) )
+			return $this->error;
+
+		$this->escape( $args );
+
+		$blog_id     = (int) $args[0];
+		$username    = $args[1];
+		$password    = $args[2];
+		$revision_id = (int) $args[3];
+
+		if ( ! $user = $this->login( $username, $password ) )
+			return $this->error;
+
+		do_action( 'xmlrpc_call', 'wp.restoreRevision' );
+
+		if ( ! $revision = wp_get_post_revision( $revision_id ) )
+			return new IXR_Error( 404, __( 'Invalid post ID' ) );
+
+		if ( wp_is_post_autosave( $revision ) )
+			return new IXR_Error( 404, __( 'Invalid post ID' ) );
+
+		if ( ! $post = get_post( $revision->post_parent ) )
+			return new IXR_Error( 404, __( 'Invalid post ID' ) );
+
+		if ( ! current_user_can( 'edit_post', $revision->post_parent ) )
+			return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
+
+		// Check if revisions are disabled.
+		if ( ! WP_POST_REVISIONS || ! post_type_supports( $post->post_type, 'revisions' ) )
+			return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) );
+
+		$post = wp_restore_post_revision( $revision_id );
+
+		return (bool) $post;
+	}
+
 	/* Blogger API functions.
 	 * specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/
 	 */
@@ -3204,7 +3645,7 @@
 			'url'      => get_option('home') . '/',
 			'blogid'   => '1',
 			'blogName' => get_option('blogname'),
-			'xmlrpc'   => site_url( 'xmlrpc.php' )
+			'xmlrpc'   => site_url( 'xmlrpc.php', 'rpc' ),
 		);
 
 		return array($struct);
@@ -3216,12 +3657,12 @@
 	 * @access protected
 	 */
 	function _multisite_getUsersBlogs($args) {
-		global $current_blog;
+		$current_blog = get_blog_details();
+
 		$domain = $current_blog->domain;
 		$path = $current_blog->path . 'xmlrpc.php';
-		$protocol = is_ssl() ? 'https' : 'http';
-
-		$rpc = new IXR_Client("$protocol://{$domain}{$path}");
+
+		$rpc = new IXR_Client( set_url_scheme( "http://{$domain}{$path}" ) );
 		$rpc->query('wp.getUsersBlogs', $args[1], $args[2]);
 		$blogs = $rpc->getResponse();
 
@@ -3294,7 +3735,7 @@
 		if ( !$user = $this->login($username, $password) )
 			return $this->error;
 
-		$post_data = wp_get_single_post($post_ID, ARRAY_A);
+		$post_data = get_post($post_ID, ARRAY_A);
 		if ( ! $post_data )
 			return new IXR_Error( 404, __( 'Invalid post ID.' ) );
 
@@ -3381,82 +3822,23 @@
 	}
 
 	/**
-	 * Retrieve blog_filename content.
+	 * Deprecated.
 	 *
 	 * @since 1.5.0
-	 *
-	 * @param array $args Method parameters.
-	 * @return string
+	 * @deprecated 3.5.0
 	 */
 	function blogger_getTemplate($args) {
-
-		$this->escape($args);
-
-		$blog_ID    = (int) $args[1];
-		$username = $args[2];
-		$password  = $args[3];
-		$template   = $args[4]; /* could be 'main' or 'archiveIndex', but we don't use it */
-
-		if ( !$user = $this->login($username, $password) )
-			return $this->error;
-
-		do_action('xmlrpc_call', 'blogger.getTemplate');
-
-		if ( !current_user_can('edit_themes') )
-			return new IXR_Error(401, __('Sorry, this user cannot edit the template.'));
-
-		/* warning: here we make the assumption that the blog's URL is on the same server */
-		$filename = get_option('home') . '/';
-		$filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);
-
-		$f = fopen($filename, 'r');
-		$content = fread($f, filesize($filename));
-		fclose($f);
-
-		/* so it is actually editable with a windows/mac client */
-		// 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);
-
-		return $content;
+		return new IXR_Error( 403, __('Sorry, that file cannot be edited.' ) );
 	}
 
 	/**
-	 * Updates the content of blog_filename.
+	 * Deprecated.
 	 *
 	 * @since 1.5.0
-	 *
-	 * @param array $args Method parameters.
-	 * @return bool True when done.
+	 * @deprecated 3.5.0
 	 */
 	function blogger_setTemplate($args) {
-
-		$this->escape($args);
-
-		$blog_ID    = (int) $args[1];
-		$username = $args[2];
-		$password  = $args[3];
-		$content    = $args[4];
-		$template   = $args[5]; /* could be 'main' or 'archiveIndex', but we don't use it */
-
-		if ( !$user = $this->login($username, $password) )
-			return $this->error;
-
-		do_action('xmlrpc_call', 'blogger.setTemplate');
-
-		if ( !current_user_can('edit_themes') )
-			return new IXR_Error(401, __('Sorry, this user cannot edit the template.'));
-
-		/* warning: here we make the assumption that the blog's URL is on the same server */
-		$filename = get_option('home') . '/';
-		$filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename);
-
-		if ($f = fopen($filename, 'w+')) {
-			fwrite($f, $content);
-			fclose($f);
-		} else {
-			return new IXR_Error(500, __('Either the file is not writable, or something wrong happened. The file has not been updated.'));
-		}
-
-		return true;
+		return new IXR_Error( 403, __('Sorry, that file cannot be edited.' ) );
 	}
 
 	/**
@@ -3483,7 +3865,7 @@
 		do_action('xmlrpc_call', 'blogger.newPost');
 
 		$cap = ($publish) ? 'publish_posts' : 'edit_posts';
-		if ( !current_user_can($cap) )
+		if ( ! current_user_can( get_post_type_object( 'post' )->cap->create_posts ) || !current_user_can($cap) )
 			return new IXR_Error(401, __('Sorry, you are not allowed to post on this site.'));
 
 		$post_status = ($publish) ? 'publish' : 'draft';
@@ -3536,7 +3918,7 @@
 
 		do_action('xmlrpc_call', 'blogger.editPost');
 
-		$actual_post = wp_get_single_post($post_ID,ARRAY_A);
+		$actual_post = get_post($post_ID,ARRAY_A);
 
 		if ( !$actual_post || $actual_post['post_type'] != 'post' )
 			return new IXR_Error(404, __('Sorry, no such post.'));
@@ -3590,7 +3972,7 @@
 
 		do_action('xmlrpc_call', 'blogger.deletePost');
 
-		$actual_post = wp_get_single_post($post_ID,ARRAY_A);
+		$actual_post = get_post($post_ID,ARRAY_A);
 
 		if ( !$actual_post || $actual_post['post_type'] != 'post' )
 			return new IXR_Error(404, __('Sorry, no such post.'));
@@ -3698,6 +4080,8 @@
 			$post_type = 'post';
 		}
 
+		if ( ! current_user_can( get_post_type_object( $post_type )->cap->create_posts ) )
+			return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts on this site.' ) );
 		if ( !current_user_can( $cap ) )
 			return new IXR_Error( 401, $error_message );
 
@@ -3730,18 +4114,18 @@
 		$post_author = $user->ID;
 
 		// If an author id was provided then use it instead.
-		if ( isset($content_struct['wp_author_id']) && ($user->ID != $content_struct['wp_author_id']) ) {
+		if ( isset( $content_struct['wp_author_id'] ) && ( $user->ID != $content_struct['wp_author_id'] ) ) {
 			switch ( $post_type ) {
 				case "post":
-					if ( !current_user_can('edit_others_posts') )
-						return(new IXR_Error(401, __('You are not allowed to post as this user')));
+					if ( !current_user_can( 'edit_others_posts' ) )
+						return( new IXR_Error( 401, __( 'You are not allowed to create posts as this user.' ) ) );
 					break;
 				case "page":
-					if ( !current_user_can('edit_others_pages') )
-						return(new IXR_Error(401, __('You are not allowed to create pages as this user')));
+					if ( !current_user_can( 'edit_others_pages' ) )
+						return( new IXR_Error( 401, __( 'You are not allowed to create pages as this user.' ) ) );
 					break;
 				default:
-					return(new IXR_Error(401, __('Invalid post type')));
+					return( new IXR_Error( 401, __( 'Invalid post type' ) ) );
 					break;
 			}
 			$author = get_userdata( $content_struct['wp_author_id'] );
@@ -3902,7 +4286,7 @@
 		// Handle post formats if assigned, value is validated earlier
 		// in this function
 		if ( isset( $content_struct['wp_post_format'] ) )
-			wp_set_post_terms( $post_ID, array( 'post-format-' . $content_struct['wp_post_format'] ), 'post_format' );
+			set_post_format( $post_ID, $content_struct['wp_post_format'] );
 
 		$post_ID = wp_insert_post( $postdata, true );
 		if ( is_wp_error( $post_ID ) )
@@ -3980,7 +4364,7 @@
 
 		do_action('xmlrpc_call', 'metaWeblog.editPost');
 
-		$postdata = wp_get_single_post( $post_ID, ARRAY_A );
+		$postdata = get_post( $post_ID, ARRAY_A );
 
 		// If there is no post data for the give post id, stop
 		// now and return an error. Other wise a new post will be
@@ -4219,7 +4603,7 @@
 		// Handle post formats if assigned, validation is handled
 		// earlier in this function
 		if ( isset( $content_struct['wp_post_format'] ) )
-			wp_set_post_terms( $post_ID, array( 'post-format-' . $content_struct['wp_post_format'] ), 'post_format' );
+			set_post_format( $post_ID, $content_struct['wp_post_format'] );
 
 		do_action( 'xmlrpc_call_success_mw_editPost', $post_ID, $args );
 
@@ -4245,7 +4629,7 @@
 		if ( !$user = $this->login($username, $password) )
 			return $this->error;
 
-		$postdata = wp_get_single_post($post_ID, ARRAY_A);
+		$postdata = get_post($post_ID, ARRAY_A);
 		if ( ! $postdata )
 			return new IXR_Error( 404, __( 'Invalid post ID.' ) );
 
@@ -4380,6 +4764,7 @@
 		if ( !$posts_list )
 			return array();
 
+		$struct = array();
 		foreach ($posts_list as $entry) {
 			if ( !current_user_can( 'edit_post', $entry['ID'] ) )
 				continue;
@@ -4568,8 +4953,13 @@
 			return new IXR_Error(500, $errorString);
 		}
 		// Construct the attachment array
-		// attach to post_id 0
 		$post_id = 0;
+		if ( ! empty( $data['post_id'] ) ) {
+			$post_id = (int) $data['post_id'];
+
+			if ( ! current_user_can( 'edit_post', $post_id ) )
+				return new IXR_Error( 401, __( 'Sorry, you cannot edit this post.' ) );
+		}
 		$attachment = array(
 			'post_title' => $name,
 			'post_content' => '',
@@ -4767,6 +5157,7 @@
 		if ( !current_user_can('edit_post', $post_ID) )
 			return new IXR_Error(401, __('Sorry, you cannot edit this post.'));
 
+		$catids = array();
 		foreach ( $categories as $cat ) {
 			$catids[] = $cat['categoryId'];
 		}
@@ -4824,7 +5215,7 @@
 
 		do_action('xmlrpc_call', 'mt.getTrackbackPings');
 
-		$actual_post = wp_get_single_post($post_ID, ARRAY_A);
+		$actual_post = get_post($post_ID, ARRAY_A);
 
 		if ( !$actual_post )
 			return new IXR_Error(404, __('Sorry, no such post.'));
@@ -4871,7 +5262,7 @@
 
 		do_action('xmlrpc_call', 'mt.publishPost');
 
-		$postdata = wp_get_single_post($post_ID, ARRAY_A);
+		$postdata = get_post($post_ID, ARRAY_A);
 		if ( ! $postdata )
 			return new IXR_Error( 404, __( 'Invalid post ID.' ) );
 
@@ -4934,7 +5325,7 @@
 			$blah = explode('/', $match[0]);
 			$post_ID = (int) $blah[1];
 			$way = 'from the path';
-		} elseif ( preg_match('#p=[0-9]{1,}#', $urltest['query'], $match) ) {
+		} elseif ( isset( $urltest['query'] ) && preg_match('#p=[0-9]{1,}#', $urltest['query'], $match) ) {
 			// the querystring defines the post_ID (?p=XXXX)
 			$blah = explode('=', $match[0]);
 			$post_ID = (int) $blah[1];
@@ -4994,7 +5385,7 @@
 		// Work around bug in strip_tags():
 		$linea = str_replace('<!DOC', '<DOC', $linea);
 		$linea = preg_replace( '/[\s\r\n\t]+/', ' ', $linea ); // normalize spaces
-		$linea = preg_replace( "/ <(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $linea );
+		$linea = preg_replace( "/<\/*(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $linea );
 
 		preg_match('|<title>([^<]*?)</title>|is', $linea, $matchtitle);
 		$title = $matchtitle[1];
@@ -5085,7 +5476,7 @@
 	  		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.'));
 		}
 
-		$actual_post = wp_get_single_post($post_ID, ARRAY_A);
+		$actual_post = get_post($post_ID, ARRAY_A);
 
 		if ( !$actual_post ) {
 			// No such post = resource not found