Completed
Push — add/jetpack-data-methods ( d7e5a6 )
by
unknown
06:57
created

Manager::build_connect_url()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 4
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * The Jetpack Connection manager class file.
4
 *
5
 * @package jetpack-connection
6
 */
7
8
namespace Automattic\Jetpack\Connection;
9
10
use Automattic\Jetpack\Connection\Manager_Interface;
11
12
/**
13
 * The Jetpack Connection Manager class that is used as a single gateway between WordPress.com
14
 * and Jetpack.
15
 */
16
class Manager implements Manager_Interface {
17
18
	const SECRETS_MISSING     = 'secrets_missing';
19
	const SECRETS_EXPIRED     = 'secrets_expired';
20
	const SECRETS_OPTION_NAME = 'jetpack_secrets';
21
22
	/**
23
	 * The procedure that should be run to generate secrets.
24
	 *
25
	 * @var Callable
26
	 */
27
	protected $secret_callable;
28
29
	/**
30
	 * Initializes all needed hooks and request handlers. Handles API calls, upload
31
	 * requests, authentication requests. Also XMLRPC options requests.
32
	 * Fallback XMLRPC is also a bridge, but probably can be a class that inherits
33
	 * this one. Among other things it should strip existing methods.
34
	 *
35
	 * @param Array $methods an array of API method names for the Connection to accept and
36
	 *                       pass on to existing callables. It's possible to specify whether
37
	 *                       each method should be available for unauthenticated calls or not.
38
	 * @see Jetpack::__construct
39
	 */
40
	public function initialize( $methods ) {
41
		$methods;
42
	}
43
44
	/**
45
	 * Returns true if the current site is connected to WordPress.com.
46
	 *
47
	 * @return Boolean is the site connected?
48
	 */
49
	public function is_active() {
50
		return false;
51
	}
52
53
	/**
54
	 * Returns true if the user with the specified identifier is connected to
55
	 * WordPress.com.
56
	 *
57
	 * @param Integer $user_id the user identifier.
58
	 * @return Boolean is the user connected?
59
	 */
60
	public function is_user_connected( $user_id ) {
61
		return $user_id;
62
	}
63
64
	/**
65
	 * Get the wpcom user data of the current|specified connected user.
66
	 *
67
	 * @param Integer $user_id the user identifier.
68
	 * @return Object the user object.
69
	 */
70
	public function get_connected_user_data( $user_id ) {
71
		return $user_id;
72
	}
73
74
	/**
75
	 * Is the user the connection owner.
76
	 *
77
	 * @param Integer $user_id the user identifier.
78
	 * @return Boolean is the user the connection owner?
79
	 */
80
	public function is_connection_owner( $user_id ) {
81
		return $user_id;
82
	}
83
84
	/**
85
	 * Unlinks the current user from the linked WordPress.com user
86
	 *
87
	 * @param Integer $user_id the user identifier.
88
	 */
89
	public static function disconnect_user( $user_id ) {
90
		return $user_id;
91
	}
92
93
	/**
94
	 * Initializes a transport server, whatever it may be, saves into the object property.
95
	 * Should be changed to be protected.
96
	 */
97
	public function initialize_server() {
98
99
	}
100
101
	/**
102
	 * Checks if the current request is properly authenticated, bails if not.
103
	 * Should be changed to be protected.
104
	 */
105
	public function require_authentication() {
106
107
	}
108
109
	/**
110
	 * Verifies the correctness of the request signature.
111
	 * Should be changed to be protected.
112
	 */
113
	public function verify_signature() {
114
115
	}
116
117
	/**
118
	 * Attempts Jetpack registration which sets up the site for connection. Should
119
	 * remain public because the call to action comes from the current site, not from
120
	 * WordPress.com.
121
	 *
122
	 * @return Integer zero on success, or a bitmask on failure.
123
	 */
124
	public function register() {
125
		return 0;
126
	}
127
128
	/**
129
	 * Returns the callable that would be used to generate secrets.
130
	 *
131
	 * @return Callable a function that returns a secure string to be used as a secret.
132
	 */
133
	protected function get_secret_callable() {
134
		if ( ! isset( $this->secret_callable ) ) {
135
			/**
136
			 * Allows modification of the callable that is used to generate connection secrets.
137
			 *
138
			 * @param Callable a function or method that returns a secret string.
139
			 */
140
			$this->secret_callable = apply_filters( 'jetpack_connection_secret_generator', 'wp_generate_password' );
141
		}
142
143
		return $this->secret_callable;
144
	}
145
146
	/**
147
	 * Generates two secret tokens and the end of life timestamp for them.
148
	 *
149
	 * @param String  $action  The action name.
150
	 * @param Integer $user_id The user identifier.
151
	 * @param Integer $exp     Expiration time in seconds.
152
	 */
153
	public function generate_secrets( $action, $user_id, $exp ) {
154
		$callable = $this->get_secret_callable();
155
156
		$secrets = \Jetpack_Options::get_raw_option(
157
			self::SECRETS_OPTION_NAME,
158
			array()
159
		);
160
161
		$secret_name = 'jetpack_' . $action . '_' . $user_id;
162
163
		if (
164
			isset( $secrets[ $secret_name ] ) &&
165
			$secrets[ $secret_name ]['exp'] > time()
166
		) {
167
			return $secrets[ $secret_name ];
168
		}
169
170
		$secret_value = array(
171
			'secret_1' => call_user_func( $callable ),
172
			'secret_2' => call_user_func( $callable ),
173
			'exp'      => time() + $exp,
174
		);
175
176
		$secrets[ $secret_name ] = $secret_value;
177
178
		\Jetpack_Options::update_raw_option( self::SECRETS_OPTION_NAME, $secrets );
179
		return $secrets[ $secret_name ];
180
	}
181
182
	/**
183
	 * Returns two secret tokens and the end of life timestamp for them.
184
	 *
185
	 * @param String  $action  The action name.
186
	 * @param Integer $user_id The user identifier.
187
	 * @return string|array an array of secrets or an error string.
188
	 */
189
	public function get_secrets( $action, $user_id ) {
190
		$secret_name = 'jetpack_' . $action . '_' . $user_id;
191
		$secrets     = \Jetpack_Options::get_raw_option(
192
			self::SECRETS_OPTION_NAME,
193
			array()
194
		);
195
196
		if ( ! isset( $secrets[ $secret_name ] ) ) {
197
			return self::SECRETS_MISSING;
198
		}
199
200
		if ( $secrets[ $secret_name ]['exp'] < time() ) {
201
			$this->delete_secrets( $action, $user_id );
202
			return self::SECRETS_EXPIRED;
203
		}
204
205
		return $secrets[ $secret_name ];
206
	}
207
208
	/**
209
	 * Deletes secret tokens in case they, for example, have expired.
210
	 *
211
	 * @param String  $action  The action name.
212
	 * @param Integer $user_id The user identifier.
213
	 */
214
	public function delete_secrets( $action, $user_id ) {
215
		$secret_name = 'jetpack_' . $action . '_' . $user_id;
216
		$secrets     = \Jetpack_Options::get_raw_option(
217
			self::SECRETS_OPTION_NAME,
218
			array()
219
		);
220
		if ( isset( $secrets[ $secret_name ] ) ) {
221
			unset( $secrets[ $secret_name ] );
222
			\Jetpack_Options::update_raw_option( self::SECRETS_OPTION_NAME, $secrets );
223
		}
224
	}
225
226
	/**
227
	 * Responds to a WordPress.com call to register the current site.
228
	 * Should be changed to protected.
229
	 */
230
	public function handle_registration() {
231
232
	}
233
234
	/**
235
	 * Responds to a WordPress.com call to authorize the current user.
236
	 * Should be changed to protected.
237
	 */
238
	public function handle_authorization() {
239
240
	}
241
242
	/**
243
	 * Builds a URL to the Jetpack connection auth page.
244
	 * This needs rethinking.
245
	 *
246
	 * @param bool        $raw If true, URL will not be escaped.
247
	 * @param bool|string $redirect If true, will redirect back to Jetpack wp-admin landing page after connection.
248
	 *                              If string, will be a custom redirect.
249
	 * @param bool|string $from If not false, adds 'from=$from' param to the connect URL.
250
	 * @param bool        $register If true, will generate a register URL regardless of the existing token, since 4.9.0.
251
	 *
252
	 * @return string Connect URL
253
	 */
254
	public function build_connect_url( $raw, $redirect, $from, $register ) {
255
		return array( $raw, $redirect, $from, $register );
256
	}
257
258
	/**
259
	 * Disconnects from the Jetpack servers.
260
	 * Forgets all connection details and tells the Jetpack servers to do the same.
261
	 */
262
	public function disconnect_site() {
263
264
	}
265
266
	/**
267
	 * This function mirrors Jetpack_Data::is_usable_domain() in the WPCOM codebase.
268
	 *
269
	 * @param string $domain The domain to check.
270
	 *
271
	 * @return bool|WP_Error
272
	 */
273
	public function is_usable_domain( $domain ) {
274
275
		// If it's empty, just fail out.
276
		if ( ! $domain ) {
277
			return new WP_Error(
278
				'fail_domain_empty',
279
				/* translators: %1$s is a domain name. */
280
				sprintf( __( 'Domain `%1$s` just failed is_usable_domain check as it is empty.', 'jetpack' ), $domain )
281
			);
282
		}
283
284
		/**
285
		 * Skips the usuable domain check when connecting a site.
286
		 *
287
		 * Allows site administrators with domains that fail gethostname-based checks to pass the request to WP.com
288
		 *
289
		 * @since 4.1.0
290
		 *
291
		 * @param bool If the check should be skipped. Default false.
292
		 */
293
		if ( apply_filters( 'jetpack_skip_usuable_domain_check', false ) ) {
294
			return true;
295
		}
296
297
		// None of the explicit localhosts.
298
		$forbidden_domains = array(
299
			'wordpress.com',
300
			'localhost',
301
			'localhost.localdomain',
302
			'127.0.0.1',
303
			'local.wordpress.test',         // VVV pattern.
304
			'local.wordpress-trunk.test',   // VVV pattern.
305
			'src.wordpress-develop.test',   // VVV pattern.
306
			'build.wordpress-develop.test', // VVV pattern.
307
		);
308
		if ( in_array( $domain, $forbidden_domains, true ) ) {
309
			return new WP_Error(
310
				'fail_domain_forbidden',
311
				sprintf(
312
					/* translators: %1$s is a domain name. */
313
					__(
314
						'Domain `%1$s` just failed is_usable_domain check as it is in the forbidden array.',
315
						'jetpack'
316
					),
317
					$domain
318
				)
319
			);
320
		}
321
322
		// No .test or .local domains.
323 View Code Duplication
		if ( preg_match( '#\.(test|local)$#i', $domain ) ) {
324
			return new WP_Error(
325
				'fail_domain_tld',
326
				sprintf(
327
					/* translators: %1$s is a domain name. */
328
					__(
329
						'Domain `%1$s` just failed is_usable_domain check as it uses an invalid top level domain.',
330
						'jetpack'
331
					),
332
					$domain
333
				)
334
			);
335
		}
336
337
		// No WPCOM subdomains.
338 View Code Duplication
		if ( preg_match( '#\.WordPress\.com$#i', $domain ) ) {
339
			return new WP_Error(
340
				'fail_subdomain_wpcom',
341
				sprintf(
342
					/* translators: %1$s is a domain name. */
343
					__(
344
						'Domain `%1$s` just failed is_usable_domain check as it is a subdomain of WordPress.com.',
345
						'jetpack'
346
					),
347
					$domain
348
				)
349
			);
350
		}
351
352
		// If PHP was compiled without support for the Filter module (very edge case).
353
		if ( ! function_exists( 'filter_var' ) ) {
354
			// Just pass back true for now, and let wpcom sort it out.
355
			return true;
356
		}
357
358
		return true;
359
	}
360
}
361