Completed
Push — try/add-jetpack-purchase-token ( a45083...336cfa )
by
unknown
160:35 queued 150:30
created

Jetpack_XMLRPC_Methods::get_purchase_token()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
1
<?php
2
/**
3
 * Jetpack XMLRPC Methods.
4
 *
5
 * Registers the Jetpack specific XMLRPC methods
6
 *
7
 * @package jetpack
8
 */
9
10
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
11
use Automattic\Jetpack\Connection\Tokens;
12
13
/**
14
 * XMLRPC Methods registration and callbacks
15
 */
16
class 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
	}
25
26
	/**
27
	 * Adds Jetpack specific methods to the methods added by the Connection package.
28
	 *
29
	 * @param array $methods Methods added by the Connection package.
30
	 */
31
	public static function xmlrpc_methods( $methods ) {
32
33
		$methods['jetpack.featuresAvailable']   = array( __CLASS__, 'features_available' );
34
		$methods['jetpack.featuresEnabled']     = array( __CLASS__, 'features_enabled' );
35
		$methods['jetpack.disconnectBlog']      = array( __CLASS__, 'disconnect_blog' );
36
		$methods['jetpack.getPurchaseToken']    = array( __CLASS__, 'get_purchase_token' );
37
		$methods['jetpack.deletePurchaseToken'] = array( __CLASS__, 'delete_purchase_token' );
38
		$methods['jetpack.jsonAPI']             = array( __CLASS__, 'json_api' );
39
40
		return $methods;
41
	}
42
43
	/**
44
	 * Returns what features are available. Uses the slug of the module files.
45
	 *
46
	 * @return array
47
	 */
48 View Code Duplication
	public static function features_available() {
49
		$raw_modules = Jetpack::get_available_modules();
50
		$modules     = array();
51
		foreach ( $raw_modules as $module ) {
52
			$modules[] = Jetpack::get_module_slug( $module );
53
		}
54
55
		return $modules;
56
	}
57
58
	/**
59
	 * Returns what features are enabled. Uses the slug of the modules files.
60
	 *
61
	 * @return array
62
	 */
63 View Code Duplication
	public static function features_enabled() {
64
		$raw_modules = Jetpack::get_active_modules();
65
		$modules     = array();
66
		foreach ( $raw_modules as $module ) {
67
			$modules[] = Jetpack::get_module_slug( $module );
68
		}
69
70
		return $modules;
71
	}
72
73
	/**
74
	 * Filters the result of test_connection XMLRPC method
75
	 *
76
	 * @return string The current Jetpack version number
77
	 */
78
	public static function test_connection() {
79
		return JETPACK__VERSION;
80
	}
81
82
	/**
83
	 * Disconnect this blog from the connected wordpress.com account
84
	 *
85
	 * @return boolean
86
	 */
87
	public static function disconnect_blog() {
88
89
		/**
90
		 * Fired when we want to log an event to the Jetpack event log.
91
		 *
92
		 * @since 7.7.0
93
		 *
94
		 * @param string $code Unique name for the event.
95
		 * @param string $data Optional data about the event.
96
		 */
97
		do_action( 'jetpack_event_log', 'disconnect' );
98
		Jetpack::disconnect();
99
100
		return true;
101
	}
102
103
	/**
104
	 * Returns a purchase token used for site-connected (non user-authenticated) checkout.
105
	 *
106
	 * @return array The current purchase token
107
	 */
108
	public static function get_purchase_token() {
109
		$blog_id = Jetpack_Options::get_option( 'id' );
110
		if ( ! $blog_id ) {
111
			return new WP_Error( 'site_not_registered', esc_html__( 'Site not registered.', 'jetpack' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'site_not_registered'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
112
		}
113
114
		$purchase_token = Jetpack_Options::get_option( 'purchase_token', false );
115
		$response       = array(
116
			'purchaseToken' => $purchase_token,
117
		);
118
119
		return $response;
120
	}
121
122
	/**
123
	 * Deletes the purchaseToken Jetpack_Option
124
	 *
125
	 * @return boolean
126
	 */
127
	public static function delete_purchase_token() {
128
		$blog_id = Jetpack_Options::get_option( 'id' );
129
		if ( ! $blog_id ) {
130
			return new WP_Error( 'site_not_registered', esc_html__( 'Site not registered.', 'jetpack' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'site_not_registered'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
131
		}
132
133
		return Jetpack_Options::delete_option( 'purchase_token' );
134
	}
135
136
	/**
137
	 * Serve a JSON API request.
138
	 *
139
	 * @param array $args request arguments.
140
	 */
141
	public static function json_api( $args = array() ) {
142
		$json_api_args        = $args[0];
143
		$verify_api_user_args = $args[1];
144
145
		$method       = (string) $json_api_args[0];
146
		$url          = (string) $json_api_args[1];
147
		$post_body    = is_null( $json_api_args[2] ) ? null : (string) $json_api_args[2];
148
		$user_details = (array) $json_api_args[4];
149
		$locale       = (string) $json_api_args[5];
150
151
		if ( ! $verify_api_user_args ) {
152
			$user_id = 0;
153
		} elseif ( 'internal' === $verify_api_user_args[0] ) {
154
			$user_id = (int) $verify_api_user_args[1];
155
			if ( $user_id ) {
156
				$user = get_user_by( 'id', $user_id );
157
				if ( ! $user || is_wp_error( $user ) ) {
158
					return false;
159
				}
160
			}
161
		} else {
162
			$user_id = call_user_func( array( new Jetpack_XMLRPC_Server(), 'test_api_user_code' ), $verify_api_user_args );
163
			if ( ! $user_id ) {
164
				return false;
165
			}
166
		}
167
168
		if ( 'en' !== $locale ) {
169
			// .org mo files are named slightly different from .com, and all we have is this the locale -- try to guess them.
170
			$new_locale = $locale;
171
			if ( strpos( $locale, '-' ) !== false ) {
172
				$locale_pieces = explode( '-', $locale );
173
				$new_locale    = $locale_pieces[0];
174
				$new_locale   .= ( ! empty( $locale_pieces[1] ) ) ? '_' . strtoupper( $locale_pieces[1] ) : '';
175
			} else {
176
				// .com might pass 'fr' because thats what our language files are named as, where core seems
177
				// to do fr_FR - so try that if we don't think we can load the file.
178
				if ( ! file_exists( WP_LANG_DIR . '/' . $locale . '.mo' ) ) {
179
					$new_locale = $locale . '_' . strtoupper( $locale );
180
				}
181
			}
182
183
			if ( file_exists( WP_LANG_DIR . '/' . $new_locale . '.mo' ) ) {
184
				unload_textdomain( 'default' );
185
				load_textdomain( 'default', WP_LANG_DIR . '/' . $new_locale . '.mo' );
186
			}
187
		}
188
189
		$old_user = wp_get_current_user();
190
		wp_set_current_user( $user_id );
191
192
		if ( $user_id ) {
193
			$token_key = false;
194
		} else {
195
			$verified  = ( new Connection_Manager() )->verify_xml_rpc_signature();
196
			$token_key = $verified['token_key'];
197
		}
198
199
		$token = ( new Tokens() )->get_access_token( $user_id, $token_key );
200
		if ( ! $token || is_wp_error( $token ) ) {
201
			return false;
202
		}
203
204
		define( 'REST_API_REQUEST', true );
205
		define( 'WPCOM_JSON_API__BASE', 'public-api.wordpress.com/rest/v1' );
206
207
		// needed?
208
		require_once ABSPATH . 'wp-admin/includes/admin.php';
209
210
		require_once JETPACK__PLUGIN_DIR . 'class.json-api.php';
211
		$api                        = WPCOM_JSON_API::init( $method, $url, $post_body );
212
		$api->token_details['user'] = $user_details;
213
		require_once JETPACK__PLUGIN_DIR . 'class.json-api-endpoints.php';
214
215
		$display_errors = ini_set( 'display_errors', 0 ); // phpcs:ignore WordPress.PHP.IniSet
216
		ob_start();
217
		$api->serve( false );
218
		$output = ob_get_clean();
219
		ini_set( 'display_errors', $display_errors ); // phpcs:ignore WordPress.PHP.IniSet
220
221
		$nonce = wp_generate_password( 10, false );
222
		$hmac  = hash_hmac( 'md5', $nonce . $output, $token->secret );
223
224
		wp_set_current_user( isset( $old_user->ID ) ? $old_user->ID : 0 );
225
226
		return array(
227
			(string) $output,
228
			(string) $nonce,
229
			(string) $hmac,
230
		);
231
	}
232
}
233