Completed
Push — add/e2e-mailchimp-block-test ( e217db...6066d0 )
by Yaroslav
98:30 queued 85:55
created

Jetpack_Protect_Blocked_Login_Page::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 23
rs 9.552
c 0
b 0
f 0
1
<?php
2
3
use Automattic\Jetpack\Connection\Client;
4
5
/**
6
 * Class Jetpack_Protect_Blocked_Login_Page
7
 *
8
 * Instanciated on the wp-login page when Jetpack modules are loaded and $pagenow
9
 * is available, or during the login_head hook.
10
 *
11
 * Class will only be instanciated if Protect has detected a hard blocked IP address.
12
 *
13
 *
14
 */
15
class Jetpack_Protect_Blocked_Login_Page {
16
17
	private static $__instance = null;
18
	public $can_send_recovery_emails;
19
	public $ip_address;
20
	public $valid_blocked_user_id;
21
	public $email_address;
22
	const HELP_URL = 'https://jetpack.com/support/security-features/#unblock';
23
	const HTTP_STATUS_CODE_TOO_MANY_REQUESTS = 429;
24
25
	/**
26
	 * Singleton implementation
27
	 *
28
	 * @return object
29
	 */
30
	public static function instance( $ip_address ) {
31
		if ( ! is_a( self::$__instance, 'Jetpack_Protect_Blocked_Login_Page' ) ) {
32
			self::$__instance = new Jetpack_Protect_Blocked_Login_Page( $ip_address );
33
		}
34
35
		return self::$__instance;
36
	}
37
38
39
	function __construct( $ip_address ) {
40
		/**
41
		 * Filter controls if an email recovery form is shown to blocked IPs.
42
		 *
43
		 * A recovery form allows folks to re-gain access to the login form
44
		 * via an email link if their IP was mistakenly blocked.
45
		 *
46
		 * @module protect
47
		 *
48
		 * @since 5.6.0
49
		 *
50
		 * @param bool $can_send_recovery_emails Defaults to true.
51
		 */
52
		$this->can_send_recovery_emails = apply_filters( 'jetpack_protect_can_send_recovery_emails', true );
53
		$this->ip_address               = $ip_address;
54
55
		add_filter( 'wp_authenticate_user', array( $this, 'check_valid_blocked_user' ), 10, 1 );
56
		add_filter( 'site_url', array( $this, 'add_args_to_login_post_url' ), 10, 3 );
57
		add_filter( 'network_site_url', array( $this, 'add_args_to_login_post_url' ), 10, 3 );
58
		add_filter( 'lostpassword_url', array( $this, 'add_args_to_lostpassword_url' ), 10, 2 );
59
		add_filter( 'login_url', array( $this, 'add_args_to_login_url' ), 10, 3 );
60
		add_filter( 'lostpassword_redirect', array( $this, 'add_args_to_lostpassword_redirect_url' ), 10, 1 );
61
	}
62
63
	public function add_args_to_lostpassword_redirect_url( $url ) {
64
		if ( $this->valid_blocked_user_id ) {
65
			$url = empty( $url ) ? wp_login_url() : $url;
66
			$url = add_query_arg(
67
				array(
68
					'validate_jetpack_protect_recovery' => $_GET['validate_jetpack_protect_recovery'],
69
					'user_id'                           => $_GET['user_id'],
70
					'checkemail'                        => 'confirm',
71
				),
72
				$url
73
			);
74
		}
75
76
		return $url;
77
	}
78
79
	public function add_args_to_lostpassword_url( $url, $redirect ) {
80
		if ( $this->valid_blocked_user_id ) {
81
			$args = array(
82
				'validate_jetpack_protect_recovery' => $_GET['validate_jetpack_protect_recovery'],
83
				'user_id'                           => $_GET['user_id'],
84
				'action'                            => 'lostpassword',
85
			);
86
			if ( ! empty( $redirect ) ) {
87
				$args['redirect_to'] = $redirect;
88
			}
89
			$url = add_query_arg( $args, $url );
90
		}
91
92
		return $url;
93
	}
94
95
	public function add_args_to_login_post_url( $url, $path, $scheme ) {
96
		if ( $this->valid_blocked_user_id && ( 'login_post' === $scheme || 'login' === $scheme ) ) {
97
			$url = add_query_arg(
98
				array(
99
					'validate_jetpack_protect_recovery' => $_GET['validate_jetpack_protect_recovery'],
100
					'user_id'                           => $_GET['user_id'],
101
				),
102
				$url
103
			);
104
105
		}
106
107
		return $url;
108
	}
109
110
	public function add_args_to_login_url( $url, $redirect, $force_reauth ) {
111
		if ( $this->valid_blocked_user_id ) {
112
			$args = array(
113
				'validate_jetpack_protect_recovery' => $_GET['validate_jetpack_protect_recovery'],
114
				'user_id'                           => $_GET['user_id'],
115
			);
116
117
			if ( ! empty( $redirect ) ) {
118
				$args['redirect_to'] = $redirect;
119
			}
120
121
			if ( ! empty( $force_reauth ) ) {
122
				$args['reauth'] = '1';
123
			}
124
			$url = add_query_arg( $args, $url );
125
		}
126
127
		return $url;
128
	}
129
130
	public function check_valid_blocked_user( $user ) {
131
		if ( $this->valid_blocked_user_id && $this->valid_blocked_user_id != $user->ID ) {
132
			return new WP_Error( 'invalid_recovery_token', __( 'The recovery token is not valid for this user.', 'jetpack' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_recovery_token'.

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...
133
		}
134
135
		return $user;
136
	}
137
138
	public function is_blocked_user_valid() {
139
		if ( ! $this->can_send_recovery_emails ) {
140
			return false;
141
		}
142
143
		if ( $this->valid_blocked_user_id ) {
144
			return true;
145
		}
146
147
		if ( ! isset( $_GET['validate_jetpack_protect_recovery'], $_GET['user_id'] ) ) {
148
			return false;
149
		}
150
151
		if ( ! $this->is_valid_protect_recovery_key( $_GET['validate_jetpack_protect_recovery'], $_GET['user_id'] ) ) {
152
			return false;
153
		}
154
155
		$this->valid_blocked_user_id = (int) $_GET['user_id'];
156
157
		return true;
158
	}
159
160
	public function is_valid_protect_recovery_key( $key, $user_id ) {
161
162
		$path     = sprintf( '/sites/%d/protect/recovery/confirm', Jetpack::get_option( 'id' ) );
163
		$response = Client::wpcom_json_api_request_as_blog(
164
			$path,
165
			'1.1',
166
			array(
167
				'method' => 'post'
168
			),
169
			array(
0 ignored issues
show
Documentation introduced by
array('token' => $key, '...' => $this->ip_address) is of type array<string,?,{"token":...user_id":"?","ip":"?"}>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
170
				'token'   => $key,
171
				'user_id' => $user_id,
172
				'ip'      => $this->ip_address,
173
			)
174
		);
175
176
		$result = json_decode( wp_remote_retrieve_body( $response ) );
177
178
		if ( is_wp_error( $result ) || empty( $result ) || isset( $result->error ) ) {
179
			return false;
180
		}
181
182
		return true;
183
	}
184
185
	public function render_and_die() {
186
		if ( ! $this->can_send_recovery_emails ) {
187
			$this->render_blocked_login_message();
188
189
			return;
190
		}
191
192
		if ( isset( $_GET['validate_jetpack_protect_recovery'] ) && $_GET['user_id'] ) {
193
			$error = new WP_Error( 'invalid_token', __( "Oops, we couldn't validate the recovery token.", 'jetpack' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_token'.

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...
194
			$this->protect_die( $error );
195
196
			return;
197
		}
198
199
		if (
200
			isset( $_GET['jetpack-protect-recovery'] ) &&
201
			isset( $_POST['_wpnonce'] ) &&
202
			wp_verify_nonce( $_POST['_wpnonce'], 'bypass-protect' )
203
		) {
204
			$this->process_recovery_email();
205
206
			return;
207
		}
208
209
		if ( isset( $_GET['loggedout'] ) && 'true' === $_GET['loggedout'] ) {
210
			$this->protect_die( __( 'You successfully logged out.', 'jetpack' ) );
211
		}
212
213
		$this->render_recovery_form();
214
	}
215
216
	public function render_blocked_login_message() {
217
		$this->protect_die( $this->get_html_blocked_login_message() );
218
	}
219
220
	function process_recovery_email() {
221
		$sent = $this->send_recovery_email();
222
		$show_recovery_form = true;
223
		if ( is_wp_error( $sent ) ) {
224
			if ( 'email_already_sent' === $sent->get_error_code() ) {
0 ignored issues
show
Bug introduced by
The method get_error_code() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
225
				$show_recovery_form = false;
226
			}
227
			$this->protect_die( $sent,null,true, $show_recovery_form );
228
		} else {
229
			$this->render_recovery_success();
230
		}
231
	}
232
233
	function send_recovery_email() {
234
		$email = isset( $_POST['email'] ) ? $_POST['email'] : '';
235
		if ( sanitize_email( $email ) !== $email || ! is_email( $email ) ) {
236
			return new WP_Error( 'invalid_email', __( "Oops, looks like that's not the right email address. Please try again!", 'jetpack' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_email'.

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...
237
		}
238
		$user = get_user_by( 'email', trim( $email ) );
239
240
		if ( ! $user ) {
241
			return new WP_Error( 'invalid_user', __( "Oops, we couldn't find a user with that email. Please try again!", 'jetpack' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_user'.

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...
242
		}
243
		$this->email_address = $email;
244
		$path                = sprintf( '/sites/%d/protect/recovery/request', Jetpack::get_option( 'id' ) );
245
246
247
		$response = Client::wpcom_json_api_request_as_blog(
248
			$path,
249
			'1.1',
250
			array(
251
				'method' => 'post'
252
			),
253
			array(
0 ignored issues
show
Documentation introduced by
array('user_id' => $user...' => $this->ip_address) is of type array<string,?,{"user_id":"?","ip":"?"}>, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
254
				'user_id' => $user->ID,
255
				'ip'      => $this->ip_address
256
			)
257
		);
258
259
		$code   = wp_remote_retrieve_response_code( $response );
260
		$result = json_decode( wp_remote_retrieve_body( $response ) );
261
262
		if ( self::HTTP_STATUS_CODE_TOO_MANY_REQUESTS === $code ) {
263
			return new WP_Error( 'email_already_sent', sprintf( __( 'Recovery instructions were sent to %s. Check your inbox!', 'jetpack' ), $this->email_address ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'email_already_sent'.

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...
264
		} else if ( is_wp_error( $result ) || empty( $result ) || isset( $result->error ) ) {
265
			return new WP_Error( 'email_send_error', __( 'Oops, we were unable to send a recovery email. Try again.', 'jetpack' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'email_send_error'.

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...
266
		}
267
268
		return true;
269
	}
270
271
	function protect_die( $content, $title = null, $back_link = false, $recovery_form = false ) {
272
		if ( empty( $title ) ) {
273
			$title = __( 'Jetpack has locked your site\'s login page.', 'jetpack' );
274
		}
275
		if ( is_wp_error( $content ) ) {
276
			$svg = '<svg class="gridicon gridicons-notice" height="24" width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm1 15h-2v-2h2v2zm0-4h-2l-.5-6h3l-.5 6z"/></g></svg>';
277
			$content = '<span class="error"> '. $svg . $content->get_error_message() . '</span>';
278
		}
279
		$content =  '<p>'. $content .'</p>';
280
281
		// If for some reason the login pop up box show up in the wp-admin.
282
		if ( isset( $_GET['interim-login'] ) ) {
283
			$content = "<style>html{ background-color: #fff; } #error-message { margin:0 auto; padding: 1em; box-shadow: none; } </style>" . $content;
284
		}
285
		$this->display_page( $title, $content, $back_link, $recovery_form );
286
287
	}
288
289
	function render_recovery_form() {
290
		$content = $this->get_html_blocked_login_message();
291
		$this->protect_die( $content, null, null, true );
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
292
	}
293
294
	function render_recovery_success() {
295
		$this->protect_die( sprintf( __( 'Recovery instructions were sent to %s. Check your inbox!', 'jetpack' ), $this->email_address ) );
296
	}
297
298
299
	function get_html_blocked_login_message() {
300
		$icon = '<svg class="gridicon gridicons-spam" style="fill:#d94f4f" height="24" width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M17 2H7L2 7v10l5 5h10l5-5V7l-5-5zm-4 15h-2v-2h2v2zm0-4h-2l-.5-6h3l-.5 6z"/></g></svg>';
301
		$ip = str_replace( 'http://', '', esc_url( 'http://' . $this->ip_address ) );
302
		return sprintf(
303
			__( '<p>Your IP address <code>%2$s</code> has been flagged for potential security violations. You can unlock your login by sending yourself a special link via email. <a href="%3$s">Learn More</a></p>', 'jetpack' ),
304
			$icon,
305
			$ip,
306
			esc_url( self::HELP_URL )
307
		);
308
	}
309
310
	function get_html_recovery_form() {
311
		ob_start(); ?>
312
        <div>
313
            <form method="post" action="?jetpack-protect-recovery=true">
314
				<?php echo wp_nonce_field( 'bypass-protect' ); ?>
315
                <p><label for="email"><?php esc_html_e( 'Your email', 'jetpack' ); ?><br/></label>
316
                    <input type="email" name="email" class="text-input"/>
317
                    <input type="submit" class="button"
318
                           value="<?php esc_attr_e( 'Send email', 'jetpack' ); ?>"/>
319
                </p>
320
            </form>
321
        </div>
322
323
		<?php
324
		$contents = ob_get_contents();
325
		ob_end_clean();
326
327
		return $contents;
328
	}
329
330
	function display_page( $title, $message, $back_button = false, $recovery_form = false ) {
331
332
		if ( ! headers_sent() ) {
333
			nocache_headers();
334
			header( 'Content-Type: text/html; charset=utf-8' );
335
		}
336
337
		$text_direction = 'ltr';
338
		if ( is_rtl() ) {
339
			$text_direction = 'rtl';
340
		}
341
		?>
342
		<!DOCTYPE html>
343
		<html xmlns="http://www.w3.org/1999/xhtml" <?php if ( function_exists( 'language_attributes' ) && function_exists( 'is_rtl' ) ) {
344
			language_attributes();
345
		} else {
346
			echo "dir='$text_direction'";
347
		} ?>>
348
		<head>
349
			<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
350
			<meta name="viewport" content="width=device-width">
351
			<?php
352
			if ( function_exists( 'wp_no_robots' ) ) {
353
				wp_no_robots();
354
			}
355
			?>
356
			<title><?php echo $title ?></title>
357
			<style type="text/css">
358
				html {
359
					background: #f6f6f6;
360
				}
361
362
				body {
363
					color: #2e4453;
364
					font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
365
					margin: 2em auto;
366
					padding: 1em 2em;
367
					max-width: 460px;
368
					text-align: left;
369
				}
370
				body.is-rtl {
371
					text-align: right;
372
				}
373
				h1 {
374
					clear: both;
375
					color: #3d596d;
376
					font-size: 24px;
377
					margin:0 0 24px 0;
378
					padding: 0;
379
					font-weight: 400;
380
				}
381
382
				#error-message {
383
					box-sizing: border-box;
384
					background: white;
385
					box-shadow: 0 0 0 1px rgba(200, 215, 225, 0.5), 0 1px 2px #e9eff3;
386
					padding: 24px;
387
				}
388
389
				#error-message img {
390
					margin: 0 auto;
391
					display: block;
392
				}
393
394
				#error-page {
395
					margin-top: 50px;
396
				}
397
398
				#error-page p {
399
					font-size: 14px;
400
					line-height: 1.5;
401
					margin: 24px 0 0;
402
				}
403
404
				#error-page code {
405
					font-family: Consolas, Monaco, monospace;
406
				}
407
408
				ul li {
409
					margin-bottom: 10px;
410
					font-size: 14px;
411
				}
412
413
				a {
414
					color: #00aadc;
415
				}
416
417
				label {
418
					font-weight: bold;
419
					font-size:16px;
420
				}
421
422
				a:hover,
423
				a:active {
424
					color: #0085be;
425
				}
426
427
				a:focus {
428
					color: #124964;
429
					-webkit-box-shadow: 0 0 0 1px #5b9dd9,
430
					0 0 2px 1px rgba(30, 140, 190, .8);
431
					box-shadow: 0 0 0 1px #5b9dd9,
432
					0 0 2px 1px rgba(30, 140, 190, .8);
433
					outline: none;
434
				}
435
436
				.button {
437
					background: #00aadc;
438
					color: white;
439
					border-color: #008ab3;
440
					border-style: solid;
441
					border-width: 1px 1px 2px;
442
					cursor: pointer;
443
					display: inline-block;
444
					margin: 0;
445
					margin-right: 0px;
446
					outline: 0;
447
					overflow: hidden;
448
					font-weight: 500;
449
					text-overflow: ellipsis;
450
					text-decoration: none;
451
					vertical-align: top;
452
					box-sizing: border-box;
453
					font-size: 14px;
454
					line-height: 21px;
455
					border-radius: 4px;
456
					padding: 7px 14px 9px;
457
					-webkit-appearance: none;
458
					-moz-appearance: none;
459
					appearance: none;
460
					font-size: 14px;
461
					width: 100%;
462
				}
463
464
				.button:hover,
465
				.button:focus {
466
					border-color: #005082;
467
					outline: none;
468
				}
469
470
				.button:focus {
471
					border-color: #005082;
472
					-webkit-box-shadow: 0 0 3px rgba(0, 115, 170, .8);
473
					box-shadow: 0 0 3px rgba(0, 115, 170, .8);
474
					outline: none;
475
				}
476
				.button::-moz-focus-inner {
477
					border: 0;
478
				}
479
480
				.button:active {
481
					border-width: 2px 1px 1px;
482
				}
483
				.gridicon {
484
					fill: currentColor;
485
					vertical-align: middle;
486
				}
487
				#error-footer {
488
					padding: 16px;
489
				}
490
				#error-footer a {
491
					text-decoration: none;
492
					line-height:20px;
493
					font-size: 14px;
494
					color: #4f748e;
495
				}
496
				#error-footer a:hover {
497
					color: #2e4453;
498
				}
499
				#error-footer .gridicon{
500
					width: 16px;
501
				}
502
				#error-footer .gridicons-help {
503
					width: 24px;
504
					margin-right:8px;
505
				}
506
507
				.is-rtl #error-footer .gridicons-help {
508
					margin-left:8px;
509
				}
510
511
				.error {
512
					background: #d94f4f;
513
					color:#FFF;
514
					display: block;
515
					border-radius: 3px;
516
					line-height: 1.5;
517
					padding: 16px;
518
					padding-left: 42px;
519
				}
520
				.is-rtl .error {
521
					padding-right: 42px;
522
				}
523
				.error .gridicon {
524
					float: left;
525
					margin-left: -32px;
526
				}
527
528
				.is-rtl .error .gridicon {
529
					float: right;
530
					margin-right: -32px;
531
				}
532
533
				.text-input {
534
					margin: 0;
535
					padding: 7px 14px;
536
					width: 100%;
537
					color: #2e4453;
538
					font-size: 16px;
539
					line-height: 1.5;
540
					border: 1px solid #c8d7e1;
541
					background-color: white;
542
					transition: all .15s ease-in-out;
543
					box-sizing: border-box;
544
					margin: 8px 0 16px;
545
				}
546
				#image {
547
					display: block;
548
					width: 200px;
549
					margin: 0 auto;
550
				}
551
				<?php
552
				$rtl_class = '';
553
				if ( 'rtl' == $text_direction ) {
554
					$rtl_class = 'class="is-rtl"';
555
					echo 'body { font-family: Tahoma, Arial; }';
556
				}
557
				?>
558
			</style>
559
		</head>
560
		<body id="error-page" <?php echo $rtl_class; ?>>
561
			<h1 id="error-title"><?php echo esc_html( $title ); ?></h1>
562
			<div id="error-message">
563
				<svg id="image" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 250 134">
564
					<path fill="#E9EFF4" d="M205.2,129.8c3.7-0.7,7.4-0.9,11.1-1.1l5.5-0.1l5.5,0c3.7,0,7.4,0.1,11.1,0.2c3.7,0.1,7.4,0.3,11.1,0.8 c0.3,0,0.5,0.3,0.5,0.6c0,0.2-0.2,0.4-0.5,0.5c-3.7,0.5-7.4,0.6-11.1,0.8c-3.7,0.1-7.4,0.2-11.1,0.2l-5.5,0l-5.5-0.1 c-3.7-0.1-7.4-0.4-11.1-1.1c-0.1,0-0.2-0.2-0.2-0.3C205,129.9,205.1,129.8,205.2,129.8"/>
565
					<path fill="#E9EFF4" d="M0.2,130.9c3-0.7,5.9-0.9,8.9-1.1l4.4-0.1l4.4,0c3,0,5.9,0.1,8.9,0.2c3,0.1,5.9,0.3,8.9,0.8 c0.3,0,0.5,0.3,0.4,0.6c0,0.2-0.2,0.4-0.4,0.4c-3,0.5-5.9,0.6-8.9,0.8c-3,0.1-5.9,0.2-8.9,0.2l-4.4,0l-4.4-0.1 c-3-0.1-5.9-0.4-8.9-1.1c-0.1,0-0.2-0.2-0.2-0.3C0,131,0.1,130.9,0.2,130.9"/>
566
					<path fill="#C8D7E2" d="M101.6,130.1H70.1V52.5c0-8.5,6.9-15.3,15.3-15.3h16.1V130.1z"/>
567
					<path fill="#0DA9DD" d="M191.5,130.1h-73.8v-5.4c0-8.9,7.2-16.1,16.1-16.1h57.7V130.1z"/>
568
					<path fill="#C7E9F5" d="M55.2,25.6l-0.1,9.8L55,57l-0.1,21.6c0,0.2,0.2,0.4,0.4,0.4c0.2,0,0.4-0.2,0.4-0.4L56.6,57l0.8-21.6 c0.1-3.3,0.2-6.5,0.3-9.8H55.2z"/>
569
					<path fill="#C7E9F5" d="M203.1,25.6l0.1,18.1c0.2,28.8,0.4,57.6,1.2,86.3c0,0.4,0.4,0.8,0.8,0.8c0.4,0,0.8-0.3,0.8-0.8 c0.8-28.8,1-57.6,1.2-86.3l0.1-18.1H203.1z"/>
570
					<path fill="#7FD3F2" d="M55.3,25.6v-8.2v-6.8c0-5.9,4-10.7,9-10.7h134c5,0,9,4.8,9,10.7v14.9H55.3z"/>
571
					<path fill="#005083" d="M210.7,25.6c-13.3,1.1-26.7,1-40,1l-40,0.2l-40-0.2c-13.3-0.1-26.7,0-40-1V25c13.3-1.1,26.7-1,40-1l40-0.2 l40,0.2c13.3,0.1,26.7,0,40,1V25.6z"/>
572
					<polygon fill="#C7E9F5" points="168.7,95.6 117.7,95.6 117.7,44.6 	"/>
573
					<path fill="#C8D7E2" d="M191.5,56.5c0,11-8.9,19.9-19.9,19.9c-11,0-19.9-8.9-19.9-19.9c0-11,8.9-19.9,19.9-19.9 C182.6,36.6,191.5,45.5,191.5,56.5"/>
574
					<path fill="#FFFFFF" d="M213.2,95.5c-3.3-5.1-3.2-16.7-3.2-28.4h-32.3c0,0-5.2,25.5,4.6,33c7.5-0.1,29.9-0.6,29.9-0.6"/>
575
					<path fill="#C8D7E2" d="M213.5,95.3l-0.1-0.1l-0.3-0.5c-0.2-0.4-0.3-0.7-0.5-1.1c-0.3-0.8-0.5-1.6-0.7-2.4c-0.1-0.5-0.2-1.1-0.3-1.6 c-0.4,0-0.8,0-1.2,0c0.5,2.1,1.1,4.3,2.4,6.1l0.2,0.2c0.2,0,0.4-0.1,0.5-0.3C213.6,95.5,213.6,95.4,213.5,95.3L213.5,95.3z"/>
576
					<path fill="#C8D7E2" d="M212.5,98.6c-0.1,0-0.2,0-0.3,0l-0.1,0H212l-0.3,0l-0.6,0l-1.3,0l-2.5,0l-5,0l-19.5,0.2 c-1.9-1.7-3.1-4.1-3.8-6.5c-0.8-2.6-1.1-5.4-1.2-8.2c-0.2-5.2,0.3-10.4,1.1-15.6l5.7-0.1c0-0.9,0-1.8,0-2.6l-4.4,0l-2.5,0 c-0.4,0-0.8,0.2-1,0.5c-0.1,0.2-0.2,0.3-0.3,0.5l-0.1,0.3l-0.2,1.2c-0.3,1.7-0.5,3.3-0.7,5c-0.3,3.3-0.5,6.7-0.4,10.1 c0.1,3.4,0.5,6.7,1.5,10c0.5,1.6,1.2,3.2,2.2,4.7c0.5,0.7,1,1.4,1.7,2c0.3,0.3,0.6,0.6,1,0.9l0.1,0.1c0.1,0,0.2,0.1,0.3,0.2 c0.2,0.1,0.5,0.1,0.6,0.1l0.6,0l20-0.6l5-0.2l2.5-0.1l1.2,0l0.3,0l0.2,0c0,0,0.3,0,0.4-0.1c0.3-0.2,0.5-0.5,0.5-0.9 C213.1,99.1,212.9,98.7,212.5,98.6z"/>
577
					<path fill="#FFFFFF" d="M223.1,84.8c-3.3-5.1-4.8-16.7-4.8-28.4h-32.3c0,0-3.5,25.5,6.3,33c7.5-0.1,29.9-0.6,29.9-0.6"/>
578
					<path fill="#C8D7E2" d="M222.9,84.9c-1.3-2.1-2.2-4.4-2.8-6.7c-0.6-2.4-1.1-4.8-1.5-7.2c-0.7-4.8-1-9.1-1-13.9l0,0l-31,0.1l0,0 c-0.4,2.8-0.5,5.1-0.5,7.9c-0.1,2.9,0,5.7,0.3,8.6c0.3,2.8,0.8,5.7,1.7,8.3c0.9,2.6,2.3,5.2,4.5,6.9l-0.4-0.1l14.9-0.2 c5-0.1,10-0.1,14.9-0.1c0.1,0,0.3,0.1,0.3,0.3c0,0.1-0.1,0.3-0.2,0.3c-5,0.2-10,0.4-14.9,0.5l-14.9,0.4c-0.1,0-0.3,0-0.4-0.1l0,0 c-2.5-1.9-3.9-4.7-5-7.4c-1-2.8-1.5-5.7-1.9-8.6c-0.3-2.9-0.4-5.8-0.4-8.8c0.1-2.9,0.2-5.8,0.6-8.8c0-0.4,0.4-0.6,0.7-0.6h0 l32.3,0.1h0c0.3,0,0.6,0.3,0.6,0.6v0c0,4.8,0.2,9.6,0.7,14.4c0.3,2.4,0.6,4.8,1.2,7.1c0.5,2.3,1.2,4.7,2.4,6.8c0,0.1,0,0.1,0,0.2 C223.1,85,223,85,222.9,84.9"/>
579
					<path fill="#C8D7E2" d="M192.1,67.1c1.6-0.9,3.4-1.2,5.1-1.3c1.7-0.2,3.5-0.2,5.2-0.2c3.5,0.1,6.9,0.2,10.3,1c0.1,0,0.2,0.2,0.2,0.3 c0,0.1-0.1,0.2-0.2,0.2c-3.4,0.2-6.9,0-10.3,0c-1.7,0-3.4,0-5.1,0c-1.7,0-3.4,0.1-5.1,0.3l0,0c-0.1,0-0.1,0-0.1-0.1 C192,67.2,192.1,67.1,192.1,67.1"/>
580
					<path fill="#C8D7E2" d="M194.1,74c1.4,0,2.7,0,4.1,0c1.4,0,2.7,0,4.1,0c2.7,0,5.4-0.1,8.2-0.2c0.1,0,0.3,0.1,0.3,0.3 c0,0.1-0.1,0.2-0.2,0.3c-1.3,0.5-2.7,0.7-4.1,0.9c-1.4,0.2-2.8,0.2-4.2,0.3c-1.4,0-2.8,0-4.2-0.2c-1.4-0.2-2.8-0.4-4.1-1.1 c-0.1,0-0.1-0.1,0-0.2C193.9,74.1,194,74,194.1,74L194.1,74z"/>
581
					<path fill="#86A6BD" d="M40.2,88.6c-0.5,0-0.8-0.4-0.9-0.9l-0.1-8.2c0-0.7,0-1.4,0-2.1c0.1-0.7,0.2-1.5,0.4-2.2c0.4-1.4,1-2.8,1.9-4 c1.7-2.5,4.3-4.3,7.1-5.1c0.7-0.2,1.5-0.3,2.2-0.5c0.7-0.1,1.5-0.1,2.2-0.1c1.3,0,2.9,0,4.4,0.4c2.9,0.7,5.6,2.5,7.4,4.9 c0.9,1.2,1.6,2.6,2.1,4c0.5,1.4,0.6,3,0.6,4.4l0,16.4c0,0.7-0.6,1.3-1.3,1.3l-6.7,0c-0.7,0-1.3-0.6-1.3-1.3v0l0-10.8l0-5.4 c0-1.4-0.7-2.8-1.8-3.5c-0.6-0.4-1.3-0.6-2-0.7c-0.7,0-1.9,0-2.5,0c-1.4,0.1-2.7,1-3.3,2.3c-0.3,0.7-0.4,1.3-0.4,2.1l0,2.7 l-0.1,5.4l0,0c0,0.5-0.4,0.9-1,0.9"/>
582
					<path fill="#FFFFFF" d="M41.1,86.9l0.1-7.3c-0.1-2.6,0.7-5,2.1-7.1c1.4-2,3.6-3.5,5.9-4.1c0.6-0.2,1.2-0.3,1.8-0.3 c0.6,0,1.2-0.1,1.9,0c1.4,0,2.5,0,3.7,0.4c2.4,0.6,4.5,2,5.9,4c0.7,1,1.3,2.1,1.6,3.2c0.4,1.2,0.5,2.3,0.5,3.7l0,15.1l0,0l-4.2,0 l0-9.5l0-5.4c0-2.2-1.2-4.4-3-5.5c-0.9-0.6-2-0.9-3.1-1c-1.1,0-1.7,0-2.9,0c-2.2,0.2-4.2,1.7-5.1,3.6c-0.5,0.9-0.7,2.1-0.6,3.1 l0,2.7l0.1,4.4l0,0L41.1,86.9L41.1,86.9"/>
583
					<path fill="#86A6BD" d="M36.3,133c-1.9,0-3.8-1.1-4.8-2.8c-0.5-0.8-0.7-1.8-0.7-2.8l0-2.4l0-9.6l-0.1-9.6l0-4.8c0-0.7,0-1.8,0.3-2.8 c0.3-1,0.9-1.8,1.7-2.5c0.8-0.6,1.7-1.1,2.7-1.3c1.1-0.2,1.8-0.1,2.6-0.1l4.8,0l9.6-0.1l19.2,0c2.1,0,4.1,1.2,5.1,3 c0.5,0.9,0.8,2,0.8,3l0,2.4l0,9.6l-0.1,9.6l0,4.8c0,0.7,0,1.8-0.4,2.8c-0.3,0.9-1,1.8-1.7,2.4c-0.8,0.6-1.7,1.1-2.7,1.2 c-1.1,0.1-1.8,0-2.6,0.1l-4.8,0l-9.6-0.1L36.3,133z"/>
584
					<path fill="#FFFFFF" d="M74.8,112.3l-0.1-9.6l0-2.4c0-0.6-0.1-1.1-0.4-1.6c-0.6-1-1.7-1.6-2.8-1.6l-19.2,0L42.7,97l-4.8,0 c-0.8,0-1.7,0-2.2,0c-0.6,0.1-1.1,0.3-1.6,0.7c-0.5,0.4-0.8,0.9-1,1.4c-0.2,0.6-0.2,1.1-0.2,2l0,4.8l-0.1,9.6l0,9.6l0,2.4 c0,0.6,0.2,1.3,0.5,1.8c0.6,1.1,1.9,1.8,3.1,1.8l19.2-0.1l9.6-0.1l4.8,0c0.8,0,1.7,0,2.2-0.1c0.6-0.1,1.2-0.4,1.6-0.8 c0.5-0.4,0.8-0.9,1-1.5c0.2-0.6,0.2-1.1,0.2-2l0-4.8L74.8,112.3z"/>
585
					<path fill="#86A6BD" d="M48.1,121.4l2.9-6.2c0.3-0.6,0.2-1.3-0.3-1.8c-1-1-1.5-2.5-1.2-4c0.3-1.7,1.7-3.1,3.4-3.4 c2.9-0.6,5.4,1.6,5.4,4.4c0,1.2-0.5,2.3-1.3,3.1c-0.5,0.5-0.6,1.2-0.3,1.8l2.9,6.2c0.1,0.2-0.1,0.5-0.3,0.5H48.4 C48.1,121.9,48,121.6,48.1,121.4"/>
586
				</svg>
587
588
				<?php echo $message; ?>
589
				<?php if ( $recovery_form ) {
590
					echo $this->get_html_recovery_form();
591
				} ?>
592
			</div>
593
			<div id="error-footer">
594
			<?php if ( $back_button && ! $recovery_form ) {
595
				if (  'rtl' == $text_direction ) {
596
					$back_button_icon = '<svg class="gridicon gridicons-arrow-right" height="24" width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8-8-8z"/></g></svg>';
597
				} else {
598
					$back_button_icon = '<svg class="gridicon gridicons-arrow-left" height="24" width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z"/></g></svg>';
599
				}
600
			?>
601
				<a href='javascript:history.back()'><?php printf( __( '%s Back' ), $back_button_icon ); ?></a>
602
			<?php } else {
603
				$help_icon = '<svg class="gridicon gridicons-help" height="24" width="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm1 16h-2v-2h2v2zm0-4.14V15h-2v-2c0-.552.448-1 1-1 1.103 0 2-.897 2-2s-.897-2-2-2-2 .897-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 1.862-1.278 3.413-3 3.86z"/></g></svg>';?>
604
					<a href="<?php echo esc_url( self::HELP_URL ); ?>" rel="noopener noreferrer" target="_blank"><?php printf( __( '%s Get help unlocking your site' ), $help_icon );?></a>
605
			<?php } ?>
606
			</div>
607
		</body>
608
		</html>
609
		<?php
610
		die();
611
	}
612
}
613