Jetpack_XMLRPC_Server

Just a sack of functions.

Defined (1)

The class is defined in the following location(s).

/class.jetpack-xmlrpc-server.php  
  1. class Jetpack_XMLRPC_Server { 
  2. /** 
  3. * The current error object 
  4. */ 
  5. public $error = null; 
  6.  
  7. /** 
  8. * Whitelist of the XML-RPC methods available to the Jetpack Server. If the 
  9. * user is not authenticated (->login()) then the methods are never added,  
  10. * so they will get a "does not exist" error. 
  11. */ 
  12. function xmlrpc_methods( $core_methods ) { 
  13. $jetpack_methods = array( 
  14. 'jetpack.jsonAPI' => array( $this, 'json_api' ),  
  15. 'jetpack.verifyAction' => array( $this, 'verify_action' ),  
  16. ); 
  17.  
  18. $user = $this->login(); 
  19.  
  20. if ( $user ) { 
  21. $jetpack_methods = array_merge( $jetpack_methods, array( 
  22. 'jetpack.testConnection' => array( $this, 'test_connection' ),  
  23. 'jetpack.testAPIUserCode' => array( $this, 'test_api_user_code' ),  
  24. 'jetpack.featuresAvailable' => array( $this, 'features_available' ),  
  25. 'jetpack.featuresEnabled' => array( $this, 'features_enabled' ),  
  26. 'jetpack.getPost' => array( $this, 'get_post' ),  
  27. 'jetpack.getPosts' => array( $this, 'get_posts' ),  
  28. 'jetpack.getComment' => array( $this, 'get_comment' ),  
  29. 'jetpack.getComments' => array( $this, 'get_comments' ),  
  30. 'jetpack.disconnectBlog' => array( $this, 'disconnect_blog' ),  
  31. 'jetpack.unlinkUser' => array( $this, 'unlink_user' ),  
  32. ) ); 
  33.  
  34. if ( isset( $core_methods['metaWeblog.editPost'] ) ) { 
  35. $jetpack_methods['metaWeblog.newMediaObject'] = $core_methods['metaWeblog.newMediaObject']; 
  36. $jetpack_methods['jetpack.updateAttachmentParent'] = array( $this, 'update_attachment_parent' ); 
  37.  
  38. /** 
  39. * Filters the XML-RPC methods available to Jetpack for authenticated users. 
  40. * @since 1.1.0 
  41. * @param array $jetpack_methods XML-RPC methods available to the Jetpack Server. 
  42. * @param array $core_methods Available core XML-RPC methods. 
  43. * @param WP_User $user Information about a given WordPress user. 
  44. */ 
  45. $jetpack_methods = apply_filters( 'jetpack_xmlrpc_methods', $jetpack_methods, $core_methods, $user ); 
  46.  
  47. /** 
  48. * Filters the XML-RPC methods available to Jetpack for unauthenticated users. 
  49. * @since 3.0.0 
  50. * @param array $jetpack_methods XML-RPC methods available to the Jetpack Server. 
  51. * @param array $core_methods Available core XML-RPC methods. 
  52. */ 
  53. return apply_filters( 'jetpack_xmlrpc_unauthenticated_methods', $jetpack_methods, $core_methods ); 
  54.  
  55. /** 
  56. * Whitelist of the bootstrap XML-RPC methods 
  57. */ 
  58. function bootstrap_xmlrpc_methods() { 
  59. return array( 
  60. 'jetpack.verifyRegistration' => array( $this, 'verify_registration' ),  
  61. ); 
  62.  
  63. /** 
  64. * Verifies that Jetpack.WordPress.com received a registration request from this site 
  65. */ 
  66. function verify_registration( $verify_secret ) { 
  67. return $this->verify_action( array( 'register', $verify_secret ) ); 
  68.  
  69. /** 
  70. * @return WP_Error|string secret_2 on success, WP_Error( error_code => error_code, error_message => error description, error_data => status code ) on failure 
  71. * Possible error_codes: 
  72. * verify_secret_1_missing 
  73. * verify_secret_1_malformed 
  74. * verify_secrets_missing: No longer have verification secrets stored 
  75. * verify_secrets_mismatch: stored secret_1 does not match secret_1 sent by Jetpack.WordPress.com 
  76. */ 
  77. function verify_action( $params ) { 
  78. $action = $params[0]; 
  79. $verify_secret = $params[1]; 
  80.  
  81. if ( empty( $verify_secret ) ) { 
  82. return $this->error( new Jetpack_Error( 'verify_secret_1_missing', sprintf( 'The required "%s" parameter is missing.', 'secret_1' ), 400 ) ); 
  83. } else if ( !is_string( $verify_secret ) ) { 
  84. return $this->error( new Jetpack_Error( 'verify_secret_1_malformed', sprintf( 'The required "%s" parameter is malformed.', 'secret_1' ), 400 ) ); 
  85.  
  86. $secrets = Jetpack_Options::get_option( $action ); 
  87. if ( !$secrets || is_wp_error( $secrets ) ) { 
  88. Jetpack_Options::delete_option( $action ); 
  89. return $this->error( new Jetpack_Error( 'verify_secrets_missing', 'Verification took too long', 400 ) ); 
  90.  
  91. @list( $secret_1, $secret_2, $secret_eol ) = explode( ':', $secrets ); 
  92. if ( empty( $secret_1 ) || empty( $secret_2 ) || empty( $secret_eol ) || $secret_eol < time() ) { 
  93. Jetpack_Options::delete_option( $action ); 
  94. return $this->error( new Jetpack_Error( 'verify_secrets_missing', 'Verification took too long', 400 ) ); 
  95.  
  96. if ( $verify_secret !== $secret_1 ) { 
  97. Jetpack_Options::delete_option( $action ); 
  98. return $this->error( new Jetpack_Error( 'verify_secrets_mismatch', 'Secret mismatch', 400 ) ); 
  99.  
  100. Jetpack_Options::delete_option( $action ); 
  101.  
  102. return $secret_2; 
  103.  
  104. /** 
  105. * Wrapper for wp_authenticate( $username, $password ); 
  106. * @return WP_User|IXR_Error 
  107. */ 
  108. function login() { 
  109. Jetpack::init()->require_jetpack_authentication(); 
  110. $user = wp_authenticate( 'username', 'password' ); 
  111. if ( is_wp_error( $user ) ) { 
  112. if ( 'authentication_failed' == $user->get_error_code() ) { // Generic error could mean most anything. 
  113. $this->error = new Jetpack_Error( 'invalid_request', 'Invalid Request', 403 ); 
  114. } else { 
  115. $this->error = $user; 
  116. return false; 
  117. } else if ( !$user ) { // Shouldn't happen. 
  118. $this->error = new Jetpack_Error( 'invalid_request', 'Invalid Request', 403 ); 
  119. return false; 
  120.  
  121. return $user; 
  122.  
  123. /** 
  124. * Returns the current error as an IXR_Error 
  125. * @return null|IXR_Error 
  126. */ 
  127. function error( $error = null ) { 
  128. if ( !is_null( $error ) ) { 
  129. $this->error = $error; 
  130.  
  131. if ( is_wp_error( $this->error ) ) { 
  132. $code = $this->error->get_error_data(); 
  133. if ( !$code ) { 
  134. $code = -10520; 
  135. $message = sprintf( 'Jetpack: [%s] %s', $this->error->get_error_code(), $this->error->get_error_message() ); 
  136. return new IXR_Error( $code, $message ); 
  137. } else if ( is_a( $this->error, 'IXR_Error' ) ) { 
  138. return $this->error; 
  139.  
  140. return false; 
  141.  
  142. /** API Methods */ 
  143.  
  144. /** 
  145. * Just authenticates with the given Jetpack credentials. 
  146. * @return bool|IXR_Error 
  147. */ 
  148. function test_connection() { 
  149. return JETPACK__VERSION; 
  150.  
  151. function test_api_user_code( $args ) { 
  152. $client_id = (int) $args[0]; 
  153. $user_id = (int) $args[1]; 
  154. $nonce = (string) $args[2]; 
  155. $verify = (string) $args[3]; 
  156.  
  157. if ( !$client_id || !$user_id || !strlen( $nonce ) || 32 !== strlen( $verify ) ) { 
  158. return false; 
  159.  
  160. $user = get_user_by( 'id', $user_id ); 
  161. if ( !$user || is_wp_error( $user ) ) { 
  162. return false; 
  163.  
  164. /** debugging 
  165. error_log( "CLIENT: $client_id" ); 
  166. error_log( "USER: $user_id" ); 
  167. error_log( "NONCE: $nonce" ); 
  168. error_log( "VERIFY: $verify" ); 
  169. */ 
  170.  
  171. $jetpack_token = Jetpack_Data::get_access_token( JETPACK_MASTER_USER ); 
  172.  
  173. $api_user_code = get_user_meta( $user_id, "jetpack_json_api_$client_id", true ); 
  174. if ( !$api_user_code ) { 
  175. return false; 
  176.  
  177. $hmac = hash_hmac( 'md5', json_encode( (object) array( 
  178. 'client_id' => (int) $client_id,  
  179. 'user_id' => (int) $user_id,  
  180. 'nonce' => (string) $nonce,  
  181. 'code' => (string) $api_user_code,  
  182. ) ), $jetpack_token->secret ); 
  183.  
  184. if ( $hmac !== $verify ) { 
  185. return false; 
  186.  
  187. return $user_id; 
  188.  
  189. /** 
  190. * Disconnect this blog from the connected wordpress.com account 
  191. * @return boolean 
  192. */ 
  193. function disconnect_blog() { 
  194. Jetpack::log( 'disconnect' ); 
  195. Jetpack::disconnect(); 
  196.  
  197. return true; 
  198.  
  199. /** 
  200. * Unlink a user from WordPress.com 
  201. * This will fail if called by the Master User. 
  202. */ 
  203. function unlink_user() { 
  204. Jetpack::log( 'unlink' ); 
  205. return Jetpack::unlink_user(); 
  206.  
  207. /** 
  208. * Returns what features are available. Uses the slug of the module files. 
  209. * @return array|IXR_Error 
  210. */ 
  211. function features_available() { 
  212. $raw_modules = Jetpack::get_available_modules(); 
  213. $modules = array(); 
  214. foreach ( $raw_modules as $module ) { 
  215. $modules[] = Jetpack::get_module_slug( $module ); 
  216.  
  217. return $modules; 
  218.  
  219. /** 
  220. * Returns what features are enabled. Uses the slug of the modules files. 
  221. * @return array|IXR_Error 
  222. */ 
  223. function features_enabled() { 
  224. $raw_modules = Jetpack::get_active_modules(); 
  225. $modules = array(); 
  226. foreach ( $raw_modules as $module ) { 
  227. $modules[] = Jetpack::get_module_slug( $module ); 
  228.  
  229. return $modules; 
  230.  
  231. function get_post( $id ) { 
  232. if ( !$id = (int) $id ) { 
  233. return false; 
  234.  
  235. $jetpack = Jetpack::init(); 
  236.  
  237. $post = $jetpack->sync->get_post( $id ); 
  238. return $post; 
  239.  
  240. function get_posts( $args ) { 
  241. list( $post_ids ) = $args; 
  242. $post_ids = array_map( 'intval', (array) $post_ids ); 
  243. $jp = Jetpack::init(); 
  244. $sync_data = $jp->sync->get_content( array( 'posts' => $post_ids ) ); 
  245.  
  246. return $sync_data; 
  247.  
  248. function get_comment( $id ) { 
  249. if ( !$id = (int) $id ) { 
  250. return false; 
  251.  
  252. $jetpack = Jetpack::init(); 
  253.  
  254. $comment = $jetpack->sync->get_comment( $id ); 
  255. if ( !is_array( $comment ) ) 
  256. return false; 
  257.  
  258. $post = $jetpack->sync->get_post( $comment['comment_post_ID'] ); 
  259. if ( !$post ) { 
  260. return false; 
  261.  
  262. return $comment; 
  263.  
  264. function get_comments( $args ) { 
  265. list( $comment_ids ) = $args; 
  266. $comment_ids = array_map( 'intval', (array) $comment_ids ); 
  267. $jp = Jetpack::init(); 
  268. $sync_data = $jp->sync->get_content( array( 'comments' => $comment_ids ) ); 
  269.  
  270. return $sync_data; 
  271.  
  272. function update_attachment_parent( $args ) { 
  273. $attachment_id = (int) $args[0]; 
  274. $parent_id = (int) $args[1]; 
  275.  
  276. return wp_update_post( array( 
  277. 'ID' => $attachment_id,  
  278. 'post_parent' => $parent_id,  
  279. ) ); 
  280.  
  281. function json_api( $args = array() ) { 
  282. $json_api_args = $args[0]; 
  283. $verify_api_user_args = $args[1]; 
  284.  
  285. $method = (string) $json_api_args[0]; 
  286. $url = (string) $json_api_args[1]; 
  287. $post_body = is_null( $json_api_args[2] ) ? null : (string) $json_api_args[2]; 
  288. $user_details = (array) $json_api_args[4]; 
  289. $locale = (string) $json_api_args[5]; 
  290.  
  291. if ( !$verify_api_user_args ) { 
  292. $user_id = 0; 
  293. } elseif ( 'internal' === $verify_api_user_args[0] ) { 
  294. $user_id = (int) $verify_api_user_args[1]; 
  295. if ( $user_id ) { 
  296. $user = get_user_by( 'id', $user_id ); 
  297. if ( !$user || is_wp_error( $user ) ) { 
  298. return false; 
  299. } else { 
  300. $user_id = call_user_func( array( $this, 'test_api_user_code' ), $verify_api_user_args ); 
  301. if ( !$user_id ) { 
  302. return false; 
  303.  
  304. /** debugging 
  305. error_log( "-- begin json api via jetpack debugging -- " ); 
  306. error_log( "METHOD: $method" ); 
  307. error_log( "URL: $url" ); 
  308. error_log( "POST BODY: $post_body" ); 
  309. error_log( "VERIFY_ARGS: " . print_r( $verify_api_user_args, 1 ) ); 
  310. error_log( "VERIFIED USER_ID: " . (int) $user_id ); 
  311. error_log( "-- end json api via jetpack debugging -- " ); 
  312. */ 
  313.  
  314. if ( 'en' !== $locale ) { 
  315. // .org mo files are named slightly different from .com, and all we have is this the locale -- try to guess them. 
  316. $new_locale = $locale; 
  317. if ( strpos( $locale, '-' ) !== false ) { 
  318. $pieces = explode( '-', $locale ); 
  319. $new_locale = $locale_pieces[0]; 
  320. $new_locale .= ( ! empty( $locale_pieces[1] ) ) ? '_' . strtoupper( $locale_pieces[1] ) : ''; 
  321. } else { 
  322. // .com might pass 'fr' because thats what our language files are named as, where core seems 
  323. // to do fr_FR - so try that if we don't think we can load the file. 
  324. if ( ! file_exists( WP_LANG_DIR . '/' . $locale . '.mo' ) ) { 
  325. $new_locale = $locale . '_' . strtoupper( $locale ); 
  326.  
  327. if ( file_exists( WP_LANG_DIR . '/' . $new_locale . '.mo' ) ) { 
  328. unload_textdomain( 'default' ); 
  329. load_textdomain( 'default', WP_LANG_DIR . '/' . $new_locale . '.mo' ); 
  330.  
  331. $old_user = wp_get_current_user(); 
  332. wp_set_current_user( $user_id ); 
  333.  
  334. $token = Jetpack_Data::get_access_token( get_current_user_id() ); 
  335. if ( !$token || is_wp_error( $token ) ) { 
  336. return false; 
  337.  
  338. define( 'REST_API_REQUEST', true ); 
  339. define( 'WPCOM_JSON_API__BASE', 'public-api.wordpress.com/rest/v1' ); 
  340.  
  341. // needed? 
  342. require_once ABSPATH . 'wp-admin/includes/admin.php'; 
  343.  
  344. require_once JETPACK__PLUGIN_DIR . 'class.json-api.php'; 
  345. $api = WPCOM_JSON_API::init( $method, $url, $post_body ); 
  346. $api->token_details['user'] = $user_details; 
  347. require_once JETPACK__PLUGIN_DIR . 'class.json-api-endpoints.php'; 
  348.  
  349. $display_errors = ini_set( 'display_errors', 0 ); 
  350. ob_start(); 
  351. $content_type = $api->serve( false ); 
  352. $output = ob_get_clean(); 
  353. ini_set( 'display_errors', $display_errors ); 
  354.  
  355. $nonce = wp_generate_password( 10, false ); 
  356. $hmac = hash_hmac( 'md5', $nonce . $output, $token->secret ); 
  357.  
  358. wp_set_current_user( isset( $old_user->ID ) ? $old_user->ID : 0 ); 
  359.  
  360. return array( 
  361. (string) $output,  
  362. (string) $nonce,  
  363. (string) $hmac,  
  364. );