Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
24.00% covered (danger)
24.00%
18 / 75
37.50% covered (danger)
37.50%
3 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_XMLRPC_Methods
24.00% covered (danger)
24.00%
18 / 75
37.50% covered (danger)
37.50%
3 / 8
372.16
0.00% covered (danger)
0.00%
0 / 1
 init
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 xmlrpc_methods
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 features_available
n/a
0 / 0
n/a
0 / 0
2
 features_enabled
n/a
0 / 0
n/a
0 / 0
2
 test_connection
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 disconnect_blog
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 json_api
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 1
156
 remote_provision_response
n/a
0 / 0
n/a
0 / 0
1
 jetpack_xmlrpc_server_event
50.00% covered (danger)
50.00%
1 / 2
0.00% covered (danger)
0.00%
0 / 1
4.12
 remote_connect_end
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 remote_register_redirect_uri
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2/**
3 * Jetpack XMLRPC Methods.
4 *
5 * Registers the Jetpack specific XMLRPC methods
6 *
7 * @package jetpack
8 */
9
10use Automattic\Jetpack\Connection\Manager as Connection_Manager;
11use Automattic\Jetpack\Connection\Tokens;
12
13/**
14 * XMLRPC Methods registration and callbacks
15 */
16class Jetpack_XMLRPC_Methods {
17
18    /**
19     * Initialize the main hooks.
20     */
21    public static function init() {
22        add_filter( 'jetpack_xmlrpc_unauthenticated_methods', array( __CLASS__, 'xmlrpc_methods' ) );
23        add_filter( 'jetpack_xmlrpc_test_connection_response', array( __CLASS__, 'test_connection' ) );
24        add_action( 'jetpack_xmlrpc_server_event', array( __CLASS__, 'jetpack_xmlrpc_server_event' ), 10, 4 );
25        add_action( 'jetpack_remote_connect_end', array( __CLASS__, 'remote_connect_end' ) );
26        add_filter( 'jetpack_xmlrpc_remote_register_redirect_uri', array( __CLASS__, 'remote_register_redirect_uri' ) );
27    }
28
29    /**
30     * Adds Jetpack specific methods to the methods added by the Connection package.
31     *
32     * @param array $methods Methods added by the Connection package.
33     */
34    public static function xmlrpc_methods( $methods ) {
35
36        $methods['jetpack.featuresAvailable'] = array( __CLASS__, 'features_available' );
37        $methods['jetpack.featuresEnabled']   = array( __CLASS__, 'features_enabled' );
38        $methods['jetpack.disconnectBlog']    = array( __CLASS__, 'disconnect_blog' );
39        $methods['jetpack.jsonAPI']           = array( __CLASS__, 'json_api' );
40
41        return $methods;
42    }
43
44    /**
45     * Returns what features are available. Uses the slug of the module files.
46     *
47     * @deprecated 13.9
48     * @see Jetpack_Core_Json_Api_Endpoints::get_features_available()
49     * @return array
50     */
51    public static function features_available() {
52        $raw_modules = Jetpack::get_available_modules();
53        $modules     = array();
54        foreach ( $raw_modules as $module ) {
55            $modules[] = Jetpack::get_module_slug( $module );
56        }
57
58        return $modules;
59    }
60
61    /**
62     * Returns what features are enabled. Uses the slug of the modules files.
63     *
64     * @deprecated 13.9
65     * @see Jetpack_Core_Json_Api_Endpoints::get_features_enabled()
66     * @return array
67     */
68    public static function features_enabled() {
69        $raw_modules = Jetpack::get_active_modules();
70        $modules     = array();
71        foreach ( $raw_modules as $module ) {
72            $modules[] = Jetpack::get_module_slug( $module );
73        }
74
75        return $modules;
76    }
77
78    /**
79     * Filters the result of test_connection XMLRPC method
80     *
81     * @return string The current Jetpack version number
82     */
83    public static function test_connection() {
84        return JETPACK__VERSION;
85    }
86
87    /**
88     * Disconnect this blog from the connected wordpress.com account
89     *
90     * @return boolean
91     */
92    public static function disconnect_blog() {
93
94        /**
95         * Fired when we want to log an event to the Jetpack event log.
96         *
97         * @since 7.7.0
98         *
99         * @param string $code Unique name for the event.
100         * @param string $data Optional data about the event.
101         */
102        do_action( 'jetpack_event_log', 'disconnect' );
103        ( new Connection_Manager( 'jetpack' ) )->disconnect_site();
104
105        return true;
106    }
107
108    /**
109     * Serve a JSON API request.
110     *
111     * @param array $args request arguments.
112     */
113    public static function json_api( $args = array() ) {
114        $json_api_args        = $args[0];
115        $verify_api_user_args = $args[1];
116
117        $method       = (string) $json_api_args[0];
118        $url          = (string) $json_api_args[1];
119        $post_body    = $json_api_args[2] === null ? null : (string) $json_api_args[2];
120        $user_details = (array) $json_api_args[4];
121        $locale       = (string) $json_api_args[5];
122
123        if ( ! $verify_api_user_args ) {
124            $user_id = 0;
125        } elseif ( 'internal' === $verify_api_user_args[0] ) {
126            $user_id = (int) $verify_api_user_args[1];
127            if ( $user_id ) {
128                $user = get_user_by( 'id', $user_id );
129                if ( ! $user || is_wp_error( $user ) ) {
130                    return false;
131                }
132            }
133        } else {
134            $user_id = call_user_func( array( new Jetpack_XMLRPC_Server(), 'test_api_user_code' ), $verify_api_user_args );
135            if ( ! $user_id ) {
136                return false;
137            }
138        }
139
140        $old_user = wp_get_current_user();
141        wp_set_current_user( $user_id );
142
143        if ( $user_id ) {
144            $token_key = false;
145        } else {
146            $verified  = ( new Connection_Manager() )->verify_xml_rpc_signature();
147            $token_key = $verified['token_key'];
148        }
149
150        $token = ( new Tokens() )->get_access_token( $user_id, $token_key );
151        if ( ! $token || is_wp_error( $token ) ) {
152            return false;
153        }
154
155        define( 'REST_API_REQUEST', true );
156        define( 'WPCOM_JSON_API__BASE', 'public-api.wordpress.com/rest/v1' );
157
158        require_once JETPACK__PLUGIN_DIR . 'class.json-api.php';
159        $api                        = WPCOM_JSON_API::init( $method, $url, $post_body );
160        $api->token_details['user'] = $user_details;
161
162        $api->init_locale( $locale );
163
164        require_once JETPACK__PLUGIN_DIR . 'class.json-api-endpoints.php';
165
166        $display_errors = ini_set( 'display_errors', 0 ); // phpcs:ignore WordPress.PHP.IniSet
167        ob_start();
168        $api->serve( false );
169        $output = ob_get_clean();
170        ini_set( 'display_errors', $display_errors ); // phpcs:ignore WordPress.PHP.IniSet
171
172        $nonce = wp_generate_password( 10, false );
173        $hmac  = hash_hmac( 'md5', $nonce . $output, $token->secret );
174
175        wp_set_current_user( isset( $old_user->ID ) ? $old_user->ID : 0 );
176
177        return array(
178            (string) $output,
179            (string) $nonce,
180            $hmac,
181        );
182    }
183
184    /**
185     * Filters the response of the remote_provision XMLRPC method
186     *
187     * @param array $response The response.
188     * @param array $request An array containing at minimum a nonce key and a local_username key.
189     *
190     * @since 9.8.0
191     * @deprecated since 13.9
192     *
193     * @return array
194     */
195    public static function remote_provision_response( $response, $request ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
196        _deprecated_function( __METHOD__, '13.9' );
197        return $response;
198    }
199
200    /**
201     * Runs Jetpack specific action in xmlrpc server events
202     *
203     * @param String  $action the action name, i.e., 'remote_authorize'.
204     * @param String  $stage  the execution stage, can be 'begin', 'success', 'error', etc.
205     * @param array   $parameters extra parameters from the event.
206     * @param WP_User $user the acting user.
207     * @return void
208     */
209    public static function jetpack_xmlrpc_server_event( $action, $stage, $parameters = array(), $user = null ) { //phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
210        if ( 'remote_register' === $action && 'begin' === $stage ) {
211            Jetpack::maybe_set_version_option();
212        }
213    }
214
215    /**
216     * Hooks into the remote_connect XMLRPC endpoint and triggers Jetpack::handle_post_authorization_actions
217     *
218     * @since 9.8.0
219     * @return void
220     */
221    public static function remote_connect_end() {
222        /** This filter is documented in class.jetpack-cli.php */
223        $enable_sso = apply_filters( 'jetpack_start_enable_sso', true );
224        Jetpack::handle_post_authorization_actions( $enable_sso, false, false );
225    }
226
227    /**
228     * Filters the Redirect URI returned by the remote_register XMLRPC method
229     *
230     * @since 9.8.0
231     *
232     * @param string $redirect_uri The Redirect URI.
233     * @return string
234     */
235    public static function remote_register_redirect_uri( $redirect_uri ) {
236        $auto_enable_sso = ( ! ( new Connection_Manager() )->has_connected_owner() || Jetpack::is_module_active( 'sso' ) );
237
238        /** This filter is documented in class.jetpack-cli.php */
239        if ( apply_filters( 'jetpack_start_enable_sso', $auto_enable_sso ) ) {
240            $redirect_uri = add_query_arg(
241                array(
242                    'action'      => 'jetpack-sso',
243                    'redirect_to' => rawurlencode( admin_url() ),
244                ),
245                wp_login_url() // TODO: come back to Jetpack dashboard?
246            );
247        }
248
249        return $redirect_uri;
250    }
251}