Issues (4967)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/wp-includes/pluggable.php (27 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * These functions can be replaced via plugins. If plugins do not redefine these
4
 * functions, then these will be used instead.
5
 *
6
 * @package WordPress
7
 */
8
9
if ( !function_exists('wp_set_current_user') ) :
10
/**
11
 * Changes the current user by ID or name.
12
 *
13
 * Set $id to null and specify a name if you do not know a user's ID.
14
 *
15
 * Some WordPress functionality is based on the current user and not based on
16
 * the signed in user. Therefore, it opens the ability to edit and perform
17
 * actions on users who aren't signed in.
18
 *
19
 * @since 2.0.3
20
 * @global WP_User $current_user The current user object which holds the user data.
21
 *
22
 * @param int    $id   User ID
23
 * @param string $name User's username
24
 * @return WP_User Current user User object
25
 */
26
function wp_set_current_user($id, $name = '') {
27
	global $current_user;
28
29
	// If `$id` matches the user who's already current, there's nothing to do.
30
	if ( isset( $current_user )
31
		&& ( $current_user instanceof WP_User )
32
		&& ( $id == $current_user->ID )
33
		&& ( null !== $id )
34
	) {
35
		return $current_user;
36
	}
37
38
	$current_user = new WP_User( $id, $name );
39
40
	setup_userdata( $current_user->ID );
41
42
	/**
43
	 * Fires after the current user is set.
44
	 *
45
	 * @since 2.0.1
46
	 */
47
	do_action( 'set_current_user' );
48
49
	return $current_user;
50
}
51
endif;
52
53
if ( !function_exists('wp_get_current_user') ) :
54
/**
55
 * Retrieve the current user object.
56
 *
57
 * Will set the current user, if the current user is not set. The current user
58
 * will be set to the logged-in person. If no user is logged-in, then it will
59
 * set the current user to 0, which is invalid and won't have any permissions.
60
 *
61
 * @since 2.0.3
62
 *
63
 * @see _wp_get_current_user()
64
 * @global WP_User $current_user Checks if the current user is set.
65
 *
66
 * @return WP_User Current WP_User instance.
67
 */
68
function wp_get_current_user() {
69
	return _wp_get_current_user();
70
}
71
endif;
72
73
if ( !function_exists('get_userdata') ) :
74
/**
75
 * Retrieve user info by user ID.
76
 *
77
 * @since 0.71
78
 *
79
 * @param int $user_id User ID
80
 * @return WP_User|false WP_User object on success, false on failure.
81
 */
82
function get_userdata( $user_id ) {
83
	return get_user_by( 'id', $user_id );
84
}
85
endif;
86
87
if ( !function_exists('get_user_by') ) :
88
/**
89
 * Retrieve user info by a given field
90
 *
91
 * @since 2.8.0
92
 * @since 4.4.0 Added 'ID' as an alias of 'id' for the `$field` parameter.
93
 *
94
 * @param string     $field The field to retrieve the user with. id | ID | slug | email | login.
95
 * @param int|string $value A value for $field. A user ID, slug, email address, or login name.
96
 * @return WP_User|false WP_User object on success, false on failure.
97
 */
98
function get_user_by( $field, $value ) {
99
	$userdata = WP_User::get_data_by( $field, $value );
100
101
	if ( !$userdata )
102
		return false;
103
104
	$user = new WP_User;
105
	$user->init( $userdata );
106
107
	return $user;
108
}
109
endif;
110
111
if ( !function_exists('cache_users') ) :
112
/**
113
 * Retrieve info for user lists to prevent multiple queries by get_userdata()
114
 *
115
 * @since 3.0.0
116
 *
117
 * @global wpdb $wpdb WordPress database abstraction object.
118
 *
119
 * @param array $user_ids User ID numbers list
120
 */
121
function cache_users( $user_ids ) {
122
	global $wpdb;
123
124
	$clean = _get_non_cached_ids( $user_ids, 'users' );
125
126
	if ( empty( $clean ) )
127
		return;
128
129
	$list = implode( ',', $clean );
130
131
	$users = $wpdb->get_results( "SELECT * FROM $wpdb->users WHERE ID IN ($list)" );
132
133
	$ids = array();
134
	foreach ( $users as $user ) {
135
		update_user_caches( $user );
136
		$ids[] = $user->ID;
137
	}
138
	update_meta_cache( 'user', $ids );
139
}
140
endif;
141
142
if ( !function_exists( 'wp_mail' ) ) :
143
/**
144
 * Send mail, similar to PHP's mail
145
 *
146
 * A true return value does not automatically mean that the user received the
147
 * email successfully. It just only means that the method used was able to
148
 * process the request without any errors.
149
 *
150
 * Using the two 'wp_mail_from' and 'wp_mail_from_name' hooks allow from
151
 * creating a from address like 'Name <[email protected]>' when both are set. If
152
 * just 'wp_mail_from' is set, then just the email address will be used with no
153
 * name.
154
 *
155
 * The default content type is 'text/plain' which does not allow using HTML.
156
 * However, you can set the content type of the email by using the
157
 * {@see 'wp_mail_content_type'} filter.
158
 *
159
 * The default charset is based on the charset used on the blog. The charset can
160
 * be set using the {@see 'wp_mail_charset'} filter.
161
 *
162
 * @since 1.2.1
163
 *
164
 * @global PHPMailer $phpmailer
165
 *
166
 * @param string|array $to          Array or comma-separated list of email addresses to send message.
167
 * @param string       $subject     Email subject
168
 * @param string       $message     Message contents
169
 * @param string|array $headers     Optional. Additional headers.
170
 * @param string|array $attachments Optional. Files to attach.
171
 * @return bool Whether the email contents were sent successfully.
172
 */
173
function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) {
174
	// Compact the input, apply the filters, and extract them back out
175
176
	/**
177
	 * Filters the wp_mail() arguments.
178
	 *
179
	 * @since 2.2.0
180
	 *
181
	 * @param array $args A compacted array of wp_mail() arguments, including the "to" email,
182
	 *                    subject, message, headers, and attachments values.
183
	 */
184
	$atts = apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments' ) );
185
186
	if ( isset( $atts['to'] ) ) {
187
		$to = $atts['to'];
188
	}
189
190
	if ( !is_array( $to ) ) {
191
		$to = explode( ',', $to );
192
	}
193
194
	if ( isset( $atts['subject'] ) ) {
195
		$subject = $atts['subject'];
196
	}
197
198
	if ( isset( $atts['message'] ) ) {
199
		$message = $atts['message'];
200
	}
201
202
	if ( isset( $atts['headers'] ) ) {
203
		$headers = $atts['headers'];
204
	}
205
206
	if ( isset( $atts['attachments'] ) ) {
207
		$attachments = $atts['attachments'];
208
	}
209
210
	if ( ! is_array( $attachments ) ) {
211
		$attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) );
212
	}
213
	global $phpmailer;
214
215
	// (Re)create it, if it's gone missing
216
	if ( ! ( $phpmailer instanceof PHPMailer ) ) {
217
		require_once ABSPATH . WPINC . '/class-phpmailer.php';
218
		require_once ABSPATH . WPINC . '/class-smtp.php';
219
		$phpmailer = new PHPMailer( true );
220
	}
221
222
	// Headers
223
	$cc = $bcc = $reply_to = array();
224
225
	if ( empty( $headers ) ) {
226
		$headers = array();
227
	} else {
228
		if ( !is_array( $headers ) ) {
229
			// Explode the headers out, so this function can take both
230
			// string headers and an array of headers.
231
			$tempheaders = explode( "\n", str_replace( "\r\n", "\n", $headers ) );
232
		} else {
233
			$tempheaders = $headers;
234
		}
235
		$headers = array();
236
237
		// If it's actually got contents
238
		if ( !empty( $tempheaders ) ) {
239
			// Iterate through the raw headers
240
			foreach ( (array) $tempheaders as $header ) {
241
				if ( strpos($header, ':') === false ) {
242
					if ( false !== stripos( $header, 'boundary=' ) ) {
243
						$parts = preg_split('/boundary=/i', trim( $header ) );
244
						$boundary = trim( str_replace( array( "'", '"' ), '', $parts[1] ) );
245
					}
246
					continue;
247
				}
248
				// Explode them out
249
				list( $name, $content ) = explode( ':', trim( $header ), 2 );
250
251
				// Cleanup crew
252
				$name    = trim( $name    );
253
				$content = trim( $content );
254
255
				switch ( strtolower( $name ) ) {
256
					// Mainly for legacy -- process a From: header if it's there
257
					case 'from':
258
						$bracket_pos = strpos( $content, '<' );
259
						if ( $bracket_pos !== false ) {
260
							// Text before the bracketed email is the "From" name.
261
							if ( $bracket_pos > 0 ) {
262
								$from_name = substr( $content, 0, $bracket_pos - 1 );
263
								$from_name = str_replace( '"', '', $from_name );
264
								$from_name = trim( $from_name );
265
							}
266
267
							$from_email = substr( $content, $bracket_pos + 1 );
268
							$from_email = str_replace( '>', '', $from_email );
269
							$from_email = trim( $from_email );
270
271
						// Avoid setting an empty $from_email.
272
						} elseif ( '' !== trim( $content ) ) {
273
							$from_email = trim( $content );
274
						}
275
						break;
276
					case 'content-type':
277
						if ( strpos( $content, ';' ) !== false ) {
278
							list( $type, $charset_content ) = explode( ';', $content );
279
							$content_type = trim( $type );
280
							if ( false !== stripos( $charset_content, 'charset=' ) ) {
281
								$charset = trim( str_replace( array( 'charset=', '"' ), '', $charset_content ) );
282
							} elseif ( false !== stripos( $charset_content, 'boundary=' ) ) {
283
								$boundary = trim( str_replace( array( 'BOUNDARY=', 'boundary=', '"' ), '', $charset_content ) );
284
								$charset = '';
285
							}
286
287
						// Avoid setting an empty $content_type.
288
						} elseif ( '' !== trim( $content ) ) {
289
							$content_type = trim( $content );
290
						}
291
						break;
292
					case 'cc':
293
						$cc = array_merge( (array) $cc, explode( ',', $content ) );
294
						break;
295
					case 'bcc':
296
						$bcc = array_merge( (array) $bcc, explode( ',', $content ) );
297
						break;
298
					case 'reply-to':
299
						$reply_to = array_merge( (array) $reply_to, explode( ',', $content ) );
300
						break;
301
					default:
302
						// Add it to our grand headers array
303
						$headers[trim( $name )] = trim( $content );
304
						break;
305
				}
306
			}
307
		}
308
	}
309
310
	// Empty out the values that may be set
311
	$phpmailer->clearAllRecipients();
312
	$phpmailer->clearAttachments();
313
	$phpmailer->clearCustomHeaders();
314
	$phpmailer->clearReplyTos();
315
316
	// From email and name
317
	// If we don't have a name from the input headers
318
	if ( !isset( $from_name ) )
319
		$from_name = 'WordPress';
320
321
	/* If we don't have an email from the input headers default to wordpress@$sitename
322
	 * Some hosts will block outgoing mail from this address if it doesn't exist but
323
	 * there's no easy alternative. Defaulting to admin_email might appear to be another
324
	 * option but some hosts may refuse to relay mail from an unknown domain. See
325
	 * https://core.trac.wordpress.org/ticket/5007.
326
	 */
327
328
	if ( !isset( $from_email ) ) {
329
		// Get the site domain and get rid of www.
330
		$sitename = strtolower( $_SERVER['SERVER_NAME'] );
331
		if ( substr( $sitename, 0, 4 ) == 'www.' ) {
332
			$sitename = substr( $sitename, 4 );
333
		}
334
335
		$from_email = 'wordpress@' . $sitename;
336
	}
337
338
	/**
339
	 * Filters the email address to send from.
340
	 *
341
	 * @since 2.2.0
342
	 *
343
	 * @param string $from_email Email address to send from.
344
	 */
345
	$from_email = apply_filters( 'wp_mail_from', $from_email );
346
347
	/**
348
	 * Filters the name to associate with the "from" email address.
349
	 *
350
	 * @since 2.3.0
351
	 *
352
	 * @param string $from_name Name associated with the "from" email address.
353
	 */
354
	$from_name = apply_filters( 'wp_mail_from_name', $from_name );
355
356
	try {
357
		$phpmailer->setFrom( $from_email, $from_name, false );
358
	} catch ( phpmailerException $e ) {
359
		$mail_error_data = compact( 'to', 'subject', 'message', 'headers', 'attachments' );
360
		$mail_error_data['phpmailer_exception_code'] = $e->getCode();
361
362
		/** This filter is documented in wp-includes/pluggable.php */
363
		do_action( 'wp_mail_failed', new WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_error_data ) );
364
365
		return false;
366
	}
367
368
	// Set mail's subject and body
369
	$phpmailer->Subject = $subject;
370
	$phpmailer->Body    = $message;
371
372
	// Set destination addresses, using appropriate methods for handling addresses
373
	$address_headers = compact( 'to', 'cc', 'bcc', 'reply_to' );
374
375
	foreach ( $address_headers as $address_header => $addresses ) {
376
		if ( empty( $addresses ) ) {
377
			continue;
378
		}
379
380
		foreach ( (array) $addresses as $address ) {
381
			try {
382
				// Break $recipient into name and address parts if in the format "Foo <[email protected]>"
383
				$recipient_name = '';
384
385
				if ( preg_match( '/(.*)<(.+)>/', $address, $matches ) ) {
386
					if ( count( $matches ) == 3 ) {
387
						$recipient_name = $matches[1];
388
						$address        = $matches[2];
389
					}
390
				}
391
392
				switch ( $address_header ) {
393
					case 'to':
394
						$phpmailer->addAddress( $address, $recipient_name );
395
						break;
396
					case 'cc':
397
						$phpmailer->addCc( $address, $recipient_name );
398
						break;
399
					case 'bcc':
400
						$phpmailer->addBcc( $address, $recipient_name );
401
						break;
402
					case 'reply_to':
403
						$phpmailer->addReplyTo( $address, $recipient_name );
404
						break;
405
				}
406
			} catch ( phpmailerException $e ) {
407
				continue;
408
			}
409
		}
410
	}
411
412
	// Set to use PHP's mail()
413
	$phpmailer->isMail();
414
415
	// Set Content-Type and charset
416
	// If we don't have a content-type from the input headers
417
	if ( !isset( $content_type ) )
418
		$content_type = 'text/plain';
419
420
	/**
421
	 * Filters the wp_mail() content type.
422
	 *
423
	 * @since 2.3.0
424
	 *
425
	 * @param string $content_type Default wp_mail() content type.
426
	 */
427
	$content_type = apply_filters( 'wp_mail_content_type', $content_type );
428
429
	$phpmailer->ContentType = $content_type;
430
431
	// Set whether it's plaintext, depending on $content_type
432
	if ( 'text/html' == $content_type )
433
		$phpmailer->isHTML( true );
434
435
	// If we don't have a charset from the input headers
436
	if ( !isset( $charset ) )
437
		$charset = get_bloginfo( 'charset' );
438
439
	// Set the content-type and charset
440
441
	/**
442
	 * Filters the default wp_mail() charset.
443
	 *
444
	 * @since 2.3.0
445
	 *
446
	 * @param string $charset Default email charset.
447
	 */
448
	$phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset );
449
450
	// Set custom headers
451
	if ( !empty( $headers ) ) {
452
		foreach ( (array) $headers as $name => $content ) {
453
			$phpmailer->addCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) );
454
		}
455
456
		if ( false !== stripos( $content_type, 'multipart' ) && ! empty($boundary) )
457
			$phpmailer->addCustomHeader( sprintf( "Content-Type: %s;\n\t boundary=\"%s\"", $content_type, $boundary ) );
458
	}
459
460
	if ( !empty( $attachments ) ) {
461
		foreach ( $attachments as $attachment ) {
462
			try {
463
				$phpmailer->addAttachment($attachment);
464
			} catch ( phpmailerException $e ) {
465
				continue;
466
			}
467
		}
468
	}
469
470
	/**
471
	 * Fires after PHPMailer is initialized.
472
	 *
473
	 * @since 2.2.0
474
	 *
475
	 * @param PHPMailer &$phpmailer The PHPMailer instance, passed by reference.
476
	 */
477
	do_action_ref_array( 'phpmailer_init', array( &$phpmailer ) );
478
479
	// Send!
480
	try {
481
		return $phpmailer->send();
482
	} catch ( phpmailerException $e ) {
483
484
		$mail_error_data = compact( 'to', 'subject', 'message', 'headers', 'attachments' );
485
		$mail_error_data['phpmailer_exception_code'] = $e->getCode();
486
487
		/**
488
		 * Fires after a phpmailerException is caught.
489
		 *
490
		 * @since 4.4.0
491
		 *
492
		 * @param WP_Error $error A WP_Error object with the phpmailerException message, and an array
493
		 *                        containing the mail recipient, subject, message, headers, and attachments.
494
		 */
495
		do_action( 'wp_mail_failed', new WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_error_data ) );
496
497
		return false;
498
	}
499
}
500
endif;
501
502
if ( !function_exists('wp_authenticate') ) :
503
/**
504
 * Authenticate a user, confirming the login credentials are valid.
505
 *
506
 * @since 2.5.0
507
 * @since 4.5.0 `$username` now accepts an email address.
508
 *
509
 * @param string $username User's username or email address.
510
 * @param string $password User's password.
511
 * @return WP_User|WP_Error WP_User object if the credentials are valid,
512
 *                          otherwise WP_Error.
513
 */
514
function wp_authenticate($username, $password) {
515
	$username = sanitize_user($username);
516
	$password = trim($password);
517
518
	/**
519
	 * Filters whether a set of user login credentials are valid.
520
	 *
521
	 * A WP_User object is returned if the credentials authenticate a user.
522
	 * WP_Error or null otherwise.
523
	 *
524
	 * @since 2.8.0
525
	 * @since 4.5.0 `$username` now accepts an email address.
526
	 *
527
	 * @param null|WP_User|WP_Error $user     WP_User if the user is authenticated.
528
	 *                                        WP_Error or null otherwise.
529
	 * @param string                $username Username or email address.
530
	 * @param string                $password User password
531
	 */
532
	$user = apply_filters( 'authenticate', null, $username, $password );
533
534
	if ( $user == null ) {
535
		// TODO what should the error message be? (Or would these even happen?)
536
		// Only needed if all authentication handlers fail to return anything.
537
		$user = new WP_Error( 'authentication_failed', __( '<strong>ERROR</strong>: Invalid username, email address or incorrect password.' ) );
538
	}
539
540
	$ignore_codes = array('empty_username', 'empty_password');
541
542
	if (is_wp_error($user) && !in_array($user->get_error_code(), $ignore_codes) ) {
543
		/**
544
		 * Fires after a user login has failed.
545
		 *
546
		 * @since 2.5.0
547
		 * @since 4.5.0 The value of `$username` can now be an email address.
548
		 *
549
		 * @param string $username Username or email address.
550
		 */
551
		do_action( 'wp_login_failed', $username );
552
	}
553
554
	return $user;
555
}
556
endif;
557
558
if ( !function_exists('wp_logout') ) :
559
/**
560
 * Log the current user out.
561
 *
562
 * @since 2.5.0
563
 */
564
function wp_logout() {
565
	wp_destroy_current_session();
566
	wp_clear_auth_cookie();
567
568
	/**
569
	 * Fires after a user is logged-out.
570
	 *
571
	 * @since 1.5.0
572
	 */
573
	do_action( 'wp_logout' );
574
}
575
endif;
576
577
if ( !function_exists('wp_validate_auth_cookie') ) :
578
/**
579
 * Validates authentication cookie.
580
 *
581
 * The checks include making sure that the authentication cookie is set and
582
 * pulling in the contents (if $cookie is not used).
583
 *
584
 * Makes sure the cookie is not expired. Verifies the hash in cookie is what is
585
 * should be and compares the two.
586
 *
587
 * @since 2.5.0
588
 *
589
 * @global int $login_grace_period
590
 *
591
 * @param string $cookie Optional. If used, will validate contents instead of cookie's
592
 * @param string $scheme Optional. The cookie scheme to use: auth, secure_auth, or logged_in
593
 * @return false|int False if invalid cookie, User ID if valid.
594
 */
595
function wp_validate_auth_cookie($cookie = '', $scheme = '') {
596
	if ( ! $cookie_elements = wp_parse_auth_cookie($cookie, $scheme) ) {
597
		/**
598
		 * Fires if an authentication cookie is malformed.
599
		 *
600
		 * @since 2.7.0
601
		 *
602
		 * @param string $cookie Malformed auth cookie.
603
		 * @param string $scheme Authentication scheme. Values include 'auth', 'secure_auth',
604
		 *                       or 'logged_in'.
605
		 */
606
		do_action( 'auth_cookie_malformed', $cookie, $scheme );
607
		return false;
608
	}
609
610
	$scheme = $cookie_elements['scheme'];
611
	$username = $cookie_elements['username'];
612
	$hmac = $cookie_elements['hmac'];
613
	$token = $cookie_elements['token'];
614
	$expired = $expiration = $cookie_elements['expiration'];
615
616
	// Allow a grace period for POST and Ajax requests
617
	if ( wp_doing_ajax() || 'POST' == $_SERVER['REQUEST_METHOD'] ) {
618
		$expired += HOUR_IN_SECONDS;
619
	}
620
621
	// Quick check to see if an honest cookie has expired
622
	if ( $expired < time() ) {
623
		/**
624
		 * Fires once an authentication cookie has expired.
625
		 *
626
		 * @since 2.7.0
627
		 *
628
		 * @param array $cookie_elements An array of data for the authentication cookie.
629
		 */
630
		do_action( 'auth_cookie_expired', $cookie_elements );
631
		return false;
632
	}
633
634
	$user = get_user_by('login', $username);
635
	if ( ! $user ) {
636
		/**
637
		 * Fires if a bad username is entered in the user authentication process.
638
		 *
639
		 * @since 2.7.0
640
		 *
641
		 * @param array $cookie_elements An array of data for the authentication cookie.
642
		 */
643
		do_action( 'auth_cookie_bad_username', $cookie_elements );
644
		return false;
645
	}
646
647
	$pass_frag = substr($user->user_pass, 8, 4);
648
649
	$key = wp_hash( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
650
651
	// If ext/hash is not present, compat.php's hash_hmac() does not support sha256.
652
	$algo = function_exists( 'hash' ) ? 'sha256' : 'sha1';
653
	$hash = hash_hmac( $algo, $username . '|' . $expiration . '|' . $token, $key );
0 ignored issues
show
It seems like $key defined by wp_hash($username . '|' ... '|' . $token, $scheme) on line 649 can also be of type false; however, hash_hmac() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
654
655
	if ( ! hash_equals( $hash, $hmac ) ) {
0 ignored issues
show
It seems like $hash defined by hash_hmac($algo, $userna...n . '|' . $token, $key) on line 653 can also be of type false; however, hash_equals() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
656
		/**
657
		 * Fires if a bad authentication cookie hash is encountered.
658
		 *
659
		 * @since 2.7.0
660
		 *
661
		 * @param array $cookie_elements An array of data for the authentication cookie.
662
		 */
663
		do_action( 'auth_cookie_bad_hash', $cookie_elements );
664
		return false;
665
	}
666
667
	$manager = WP_Session_Tokens::get_instance( $user->ID );
668
	if ( ! $manager->verify( $token ) ) {
669
		do_action( 'auth_cookie_bad_session_token', $cookie_elements );
670
		return false;
671
	}
672
673
	// Ajax/POST grace period set above
674
	if ( $expiration < time() ) {
675
		$GLOBALS['login_grace_period'] = 1;
676
	}
677
678
	/**
679
	 * Fires once an authentication cookie has been validated.
680
	 *
681
	 * @since 2.7.0
682
	 *
683
	 * @param array   $cookie_elements An array of data for the authentication cookie.
684
	 * @param WP_User $user            User object.
685
	 */
686
	do_action( 'auth_cookie_valid', $cookie_elements, $user );
687
688
	return $user->ID;
689
}
690
endif;
691
692
if ( !function_exists('wp_generate_auth_cookie') ) :
693
/**
694
 * Generate authentication cookie contents.
695
 *
696
 * @since 2.5.0
697
 *
698
 * @param int    $user_id    User ID
699
 * @param int    $expiration The time the cookie expires as a UNIX timestamp.
700
 * @param string $scheme     Optional. The cookie scheme to use: auth, secure_auth, or logged_in
701
 * @param string $token      User's session token to use for this cookie
702
 * @return string Authentication cookie contents. Empty string if user does not exist.
703
 */
704
function wp_generate_auth_cookie( $user_id, $expiration, $scheme = 'auth', $token = '' ) {
705
	$user = get_userdata($user_id);
706
	if ( ! $user ) {
707
		return '';
708
	}
709
710
	if ( ! $token ) {
711
		$manager = WP_Session_Tokens::get_instance( $user_id );
712
		$token = $manager->create( $expiration );
713
	}
714
715
	$pass_frag = substr($user->user_pass, 8, 4);
716
717
	$key = wp_hash( $user->user_login . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
718
719
	// If ext/hash is not present, compat.php's hash_hmac() does not support sha256.
720
	$algo = function_exists( 'hash' ) ? 'sha256' : 'sha1';
721
	$hash = hash_hmac( $algo, $user->user_login . '|' . $expiration . '|' . $token, $key );
0 ignored issues
show
It seems like $key defined by wp_hash($user->user_logi... '|' . $token, $scheme) on line 717 can also be of type false; however, hash_hmac() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
722
723
	$cookie = $user->user_login . '|' . $expiration . '|' . $token . '|' . $hash;
724
725
	/**
726
	 * Filters the authentication cookie.
727
	 *
728
	 * @since 2.5.0
729
	 *
730
	 * @param string $cookie     Authentication cookie.
731
	 * @param int    $user_id    User ID.
732
	 * @param int    $expiration The time the cookie expires as a UNIX timestamp.
733
	 * @param string $scheme     Cookie scheme used. Accepts 'auth', 'secure_auth', or 'logged_in'.
734
	 * @param string $token      User's session token used.
735
	 */
736
	return apply_filters( 'auth_cookie', $cookie, $user_id, $expiration, $scheme, $token );
737
}
738
endif;
739
740
if ( !function_exists('wp_parse_auth_cookie') ) :
741
/**
742
 * Parse a cookie into its components
743
 *
744
 * @since 2.7.0
745
 *
746
 * @param string $cookie
747
 * @param string $scheme Optional. The cookie scheme to use: auth, secure_auth, or logged_in
748
 * @return array|false Authentication cookie components
749
 */
750
function wp_parse_auth_cookie($cookie = '', $scheme = '') {
751
	if ( empty($cookie) ) {
752
		switch ($scheme){
753
			case 'auth':
754
				$cookie_name = AUTH_COOKIE;
755
				break;
756
			case 'secure_auth':
757
				$cookie_name = SECURE_AUTH_COOKIE;
758
				break;
759
			case "logged_in":
760
				$cookie_name = LOGGED_IN_COOKIE;
761
				break;
762
			default:
763
				if ( is_ssl() ) {
764
					$cookie_name = SECURE_AUTH_COOKIE;
765
					$scheme = 'secure_auth';
766
				} else {
767
					$cookie_name = AUTH_COOKIE;
768
					$scheme = 'auth';
769
				}
770
	    }
771
772
		if ( empty($_COOKIE[$cookie_name]) )
773
			return false;
774
		$cookie = $_COOKIE[$cookie_name];
775
	}
776
777
	$cookie_elements = explode('|', $cookie);
778
	if ( count( $cookie_elements ) !== 4 ) {
779
		return false;
780
	}
781
782
	list( $username, $expiration, $token, $hmac ) = $cookie_elements;
783
784
	return compact( 'username', 'expiration', 'token', 'hmac', 'scheme' );
785
}
786
endif;
787
788
if ( !function_exists('wp_set_auth_cookie') ) :
789
/**
790
 * Log in a user by setting authentication cookies.
791
 *
792
 * The $remember parameter increases the time that the cookie will be kept. The
793
 * default the cookie is kept without remembering is two days. When $remember is
794
 * set, the cookies will be kept for 14 days or two weeks.
795
 *
796
 * @since 2.5.0
797
 * @since 4.3.0 Added the `$token` parameter.
798
 *
799
 * @param int    $user_id  User ID
800
 * @param bool   $remember Whether to remember the user
801
 * @param mixed  $secure   Whether the admin cookies should only be sent over HTTPS.
802
 *                         Default is_ssl().
803
 * @param string $token    Optional. User's session token to use for this cookie.
804
 */
805
function wp_set_auth_cookie( $user_id, $remember = false, $secure = '', $token = '' ) {
806
	if ( $remember ) {
807
		/**
808
		 * Filters the duration of the authentication cookie expiration period.
809
		 *
810
		 * @since 2.8.0
811
		 *
812
		 * @param int  $length   Duration of the expiration period in seconds.
813
		 * @param int  $user_id  User ID.
814
		 * @param bool $remember Whether to remember the user login. Default false.
815
		 */
816
		$expiration = time() + apply_filters( 'auth_cookie_expiration', 14 * DAY_IN_SECONDS, $user_id, $remember );
817
818
		/*
819
		 * Ensure the browser will continue to send the cookie after the expiration time is reached.
820
		 * Needed for the login grace period in wp_validate_auth_cookie().
821
		 */
822
		$expire = $expiration + ( 12 * HOUR_IN_SECONDS );
823
	} else {
824
		/** This filter is documented in wp-includes/pluggable.php */
825
		$expiration = time() + apply_filters( 'auth_cookie_expiration', 2 * DAY_IN_SECONDS, $user_id, $remember );
826
		$expire = 0;
827
	}
828
829
	if ( '' === $secure ) {
830
		$secure = is_ssl();
831
	}
832
833
	// Front-end cookie is secure when the auth cookie is secure and the site's home URL is forced HTTPS.
834
	$secure_logged_in_cookie = $secure && 'https' === parse_url( get_option( 'home' ), PHP_URL_SCHEME );
835
836
	/**
837
	 * Filters whether the connection is secure.
838
	 *
839
	 * @since 3.1.0
840
	 *
841
	 * @param bool $secure  Whether the connection is secure.
842
	 * @param int  $user_id User ID.
843
	 */
844
	$secure = apply_filters( 'secure_auth_cookie', $secure, $user_id );
845
846
	/**
847
	 * Filters whether to use a secure cookie when logged-in.
848
	 *
849
	 * @since 3.1.0
850
	 *
851
	 * @param bool $secure_logged_in_cookie Whether to use a secure cookie when logged-in.
852
	 * @param int  $user_id                 User ID.
853
	 * @param bool $secure                  Whether the connection is secure.
854
	 */
855
	$secure_logged_in_cookie = apply_filters( 'secure_logged_in_cookie', $secure_logged_in_cookie, $user_id, $secure );
856
857
	if ( $secure ) {
858
		$auth_cookie_name = SECURE_AUTH_COOKIE;
859
		$scheme = 'secure_auth';
860
	} else {
861
		$auth_cookie_name = AUTH_COOKIE;
862
		$scheme = 'auth';
863
	}
864
865
	if ( '' === $token ) {
866
		$manager = WP_Session_Tokens::get_instance( $user_id );
867
		$token   = $manager->create( $expiration );
868
	}
869
870
	$auth_cookie = wp_generate_auth_cookie( $user_id, $expiration, $scheme, $token );
871
	$logged_in_cookie = wp_generate_auth_cookie( $user_id, $expiration, 'logged_in', $token );
872
873
	/**
874
	 * Fires immediately before the authentication cookie is set.
875
	 *
876
	 * @since 2.5.0
877
	 *
878
	 * @param string $auth_cookie Authentication cookie.
879
	 * @param int    $expire      The time the login grace period expires as a UNIX timestamp.
880
	 *                            Default is 12 hours past the cookie's expiration time.
881
	 * @param int    $expiration  The time when the authentication cookie expires as a UNIX timestamp.
882
	 *                            Default is 14 days from now.
883
	 * @param int    $user_id     User ID.
884
	 * @param string $scheme      Authentication scheme. Values include 'auth', 'secure_auth', or 'logged_in'.
885
	 */
886
	do_action( 'set_auth_cookie', $auth_cookie, $expire, $expiration, $user_id, $scheme );
887
888
	/**
889
	 * Fires immediately before the logged-in authentication cookie is set.
890
	 *
891
	 * @since 2.6.0
892
	 *
893
	 * @param string $logged_in_cookie The logged-in cookie.
894
	 * @param int    $expire           The time the login grace period expires as a UNIX timestamp.
895
	 *                                 Default is 12 hours past the cookie's expiration time.
896
	 * @param int    $expiration       The time when the logged-in authentication cookie expires as a UNIX timestamp.
897
	 *                                 Default is 14 days from now.
898
	 * @param int    $user_id          User ID.
899
	 * @param string $scheme           Authentication scheme. Default 'logged_in'.
900
	 */
901
	do_action( 'set_logged_in_cookie', $logged_in_cookie, $expire, $expiration, $user_id, 'logged_in' );
902
903
	/**
904
	 * Allows preventing auth cookies from actually being sent to the client.
905
	 *
906
	 * @since 4.7.4
907
	 *
908
	 * @param bool $send Whether to send auth cookies to the client.
909
	 */
910
	if ( ! apply_filters( 'send_auth_cookies', true ) ) {
911
		return;
912
	}
913
914
	setcookie($auth_cookie_name, $auth_cookie, $expire, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN, $secure, true);
915
	setcookie($auth_cookie_name, $auth_cookie, $expire, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, $secure, true);
916
	setcookie(LOGGED_IN_COOKIE, $logged_in_cookie, $expire, COOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true);
917
	if ( COOKIEPATH != SITECOOKIEPATH )
918
		setcookie(LOGGED_IN_COOKIE, $logged_in_cookie, $expire, SITECOOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true);
919
}
920
endif;
921
922
if ( !function_exists('wp_clear_auth_cookie') ) :
923
/**
924
 * Removes all of the cookies associated with authentication.
925
 *
926
 * @since 2.5.0
927
 */
928
function wp_clear_auth_cookie() {
929
	/**
930
	 * Fires just before the authentication cookies are cleared.
931
	 *
932
	 * @since 2.7.0
933
	 */
934
	do_action( 'clear_auth_cookie' );
935
936
	/** This filter is documented in wp-includes/pluggable.php */
937
	if ( ! apply_filters( 'send_auth_cookies', true ) ) {
938
		return;
939
	}
940
941
	// Auth cookies
942
	setcookie( AUTH_COOKIE,        ' ', time() - YEAR_IN_SECONDS, ADMIN_COOKIE_PATH,   COOKIE_DOMAIN );
943
	setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, ADMIN_COOKIE_PATH,   COOKIE_DOMAIN );
944
	setcookie( AUTH_COOKIE,        ' ', time() - YEAR_IN_SECONDS, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN );
945
	setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN );
946
	setcookie( LOGGED_IN_COOKIE,   ' ', time() - YEAR_IN_SECONDS, COOKIEPATH,          COOKIE_DOMAIN );
947
	setcookie( LOGGED_IN_COOKIE,   ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH,      COOKIE_DOMAIN );
948
949
	// Settings cookies
950
	setcookie( 'wp-settings-' . get_current_user_id(),      ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH );
951
	setcookie( 'wp-settings-time-' . get_current_user_id(), ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH );
952
953
	// Old cookies
954
	setcookie( AUTH_COOKIE,        ' ', time() - YEAR_IN_SECONDS, COOKIEPATH,     COOKIE_DOMAIN );
955
	setcookie( AUTH_COOKIE,        ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
956
	setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH,     COOKIE_DOMAIN );
957
	setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
958
959
	// Even older cookies
960
	setcookie( USER_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH,     COOKIE_DOMAIN );
961
	setcookie( PASS_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH,     COOKIE_DOMAIN );
962
	setcookie( USER_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
963
	setcookie( PASS_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
964
}
965
endif;
966
967
if ( !function_exists('is_user_logged_in') ) :
968
/**
969
 * Checks if the current visitor is a logged in user.
970
 *
971
 * @since 2.0.0
972
 *
973
 * @return bool True if user is logged in, false if not logged in.
974
 */
975
function is_user_logged_in() {
976
	$user = wp_get_current_user();
977
978
	return $user->exists();
979
}
980
endif;
981
982
if ( !function_exists('auth_redirect') ) :
983
/**
984
 * Checks if a user is logged in, if not it redirects them to the login page.
985
 *
986
 * @since 1.5.0
987
 */
988
function auth_redirect() {
989
	// Checks if a user is logged in, if not redirects them to the login page
990
991
	$secure = ( is_ssl() || force_ssl_admin() );
992
993
	/**
994
	 * Filters whether to use a secure authentication redirect.
995
	 *
996
	 * @since 3.1.0
997
	 *
998
	 * @param bool $secure Whether to use a secure authentication redirect. Default false.
999
	 */
1000
	$secure = apply_filters( 'secure_auth_redirect', $secure );
1001
1002
	// If https is required and request is http, redirect
1003 View Code Duplication
	if ( $secure && !is_ssl() && false !== strpos($_SERVER['REQUEST_URI'], 'wp-admin') ) {
1004
		if ( 0 === strpos( $_SERVER['REQUEST_URI'], 'http' ) ) {
1005
			wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) );
1006
			exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The function auth_redirect() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1007
		} else {
1008
			wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1009
			exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The function auth_redirect() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1010
		}
1011
	}
1012
1013
	/**
1014
	 * Filters the authentication redirect scheme.
1015
	 *
1016
	 * @since 2.9.0
1017
	 *
1018
	 * @param string $scheme Authentication redirect scheme. Default empty.
1019
	 */
1020
	$scheme = apply_filters( 'auth_redirect_scheme', '' );
1021
1022
	if ( $user_id = wp_validate_auth_cookie( '',  $scheme) ) {
1023
		/**
1024
		 * Fires before the authentication redirect.
1025
		 *
1026
		 * @since 2.8.0
1027
		 *
1028
		 * @param int $user_id User ID.
1029
		 */
1030
		do_action( 'auth_redirect', $user_id );
1031
1032
		// If the user wants ssl but the session is not ssl, redirect.
1033 View Code Duplication
		if ( !$secure && get_user_option('use_ssl', $user_id) && false !== strpos($_SERVER['REQUEST_URI'], 'wp-admin') ) {
1034
			if ( 0 === strpos( $_SERVER['REQUEST_URI'], 'http' ) ) {
1035
				wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) );
1036
				exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The function auth_redirect() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1037
			} else {
1038
				wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1039
				exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The function auth_redirect() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1040
			}
1041
		}
1042
1043
		return;  // The cookie is good so we're done
1044
	}
1045
1046
	// The cookie is no good so force login
1047
	nocache_headers();
1048
1049
	$redirect = ( strpos( $_SERVER['REQUEST_URI'], '/options.php' ) && wp_get_referer() ) ? wp_get_referer() : set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1050
1051
	$login_url = wp_login_url($redirect, true);
1052
1053
	wp_redirect($login_url);
1054
	exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The function auth_redirect() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1055
}
1056
endif;
1057
1058
if ( !function_exists('check_admin_referer') ) :
1059
/**
1060
 * Makes sure that a user was referred from another admin page.
1061
 *
1062
 * To avoid security exploits.
1063
 *
1064
 * @since 1.2.0
1065
 *
1066
 * @param int|string $action    Action nonce.
0 ignored issues
show
Consider making the type for parameter $action a bit more specific; maybe use integer.
Loading history...
1067
 * @param string     $query_arg Optional. Key to check for nonce in `$_REQUEST` (since 2.5).
1068
 *                              Default '_wpnonce'.
1069
 * @return false|int False if the nonce is invalid, 1 if the nonce is valid and generated between
0 ignored issues
show
Should the return type not be null|integer|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
1070
 *                   0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago.
1071
 */
1072
function check_admin_referer( $action = -1, $query_arg = '_wpnonce' ) {
1073
	if ( -1 == $action )
1074
		_doing_it_wrong( __FUNCTION__, __( 'You should specify a nonce action to be verified by using the first parameter.' ), '3.2.0' );
1075
1076
	$adminurl = strtolower(admin_url());
1077
	$referer = strtolower(wp_get_referer());
1078
	$result = isset($_REQUEST[$query_arg]) ? wp_verify_nonce($_REQUEST[$query_arg], $action) : false;
1079
1080
	/**
1081
	 * Fires once the admin request has been validated or not.
1082
	 *
1083
	 * @since 1.5.1
1084
	 *
1085
	 * @param string    $action The nonce action.
1086
	 * @param false|int $result False if the nonce is invalid, 1 if the nonce is valid and generated between
1087
	 *                          0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago.
1088
	 */
1089
	do_action( 'check_admin_referer', $action, $result );
1090
1091
	if ( ! $result && ! ( -1 == $action && strpos( $referer, $adminurl ) === 0 ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type integer|false is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1092
		wp_nonce_ays( $action );
1093
		die();
0 ignored issues
show
Coding Style Compatibility introduced by
The function check_admin_referer() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1094
	}
1095
1096
	return $result;
1097
}
1098
endif;
1099
1100
if ( !function_exists('check_ajax_referer') ) :
1101
/**
1102
 * Verifies the Ajax request to prevent processing requests external of the blog.
1103
 *
1104
 * @since 2.0.3
1105
 *
1106
 * @param int|string   $action    Action nonce.
0 ignored issues
show
Consider making the type for parameter $action a bit more specific; maybe use integer.
Loading history...
1107
 * @param false|string $query_arg Optional. Key to check for the nonce in `$_REQUEST` (since 2.5). If false,
1108
 *                                `$_REQUEST` values will be evaluated for '_ajax_nonce', and '_wpnonce'
1109
 *                                (in that order). Default false.
1110
 * @param bool         $die       Optional. Whether to die early when the nonce cannot be verified.
1111
 *                                Default true.
1112
 * @return false|int False if the nonce is invalid, 1 if the nonce is valid and generated between
0 ignored issues
show
Should the return type not be null|false|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
1113
 *                   0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago.
1114
 */
1115
function check_ajax_referer( $action = -1, $query_arg = false, $die = true ) {
1116
	if ( -1 == $action ) {
1117
		_doing_it_wrong( __FUNCTION__, __( 'You should specify a nonce action to be verified by using the first parameter.' ), '4.7' );
1118
	}
1119
1120
	$nonce = '';
1121
1122
	if ( $query_arg && isset( $_REQUEST[ $query_arg ] ) )
0 ignored issues
show
Bug Best Practice introduced by
The expression $query_arg of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1123
		$nonce = $_REQUEST[ $query_arg ];
1124
	elseif ( isset( $_REQUEST['_ajax_nonce'] ) )
1125
		$nonce = $_REQUEST['_ajax_nonce'];
1126
	elseif ( isset( $_REQUEST['_wpnonce'] ) )
1127
		$nonce = $_REQUEST['_wpnonce'];
1128
1129
	$result = wp_verify_nonce( $nonce, $action );
1130
1131
	/**
1132
	 * Fires once the Ajax request has been validated or not.
1133
	 *
1134
	 * @since 2.1.0
1135
	 *
1136
	 * @param string    $action The Ajax nonce action.
1137
	 * @param false|int $result False if the nonce is invalid, 1 if the nonce is valid and generated between
1138
	 *                          0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago.
1139
	 */
1140
	do_action( 'check_ajax_referer', $action, $result );
1141
1142
	if ( $die && false === $result ) {
1143
		if ( wp_doing_ajax() ) {
1144
			wp_die( -1, 403 );
1145
		} else {
1146
			die( '-1' );
0 ignored issues
show
Coding Style Compatibility introduced by
The function check_ajax_referer() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1147
		}
1148
	}
1149
1150
	return $result;
1151
}
1152
endif;
1153
1154
if ( !function_exists('wp_redirect') ) :
1155
/**
1156
 * Redirects to another page.
1157
 *
1158
 * Note: wp_redirect() does not exit automatically, and should almost always be
1159
 * followed by a call to `exit;`:
1160
 *
1161
 *     wp_redirect( $url );
1162
 *     exit;
1163
 *
1164
 * Exiting can also be selectively manipulated by using wp_redirect() as a conditional
1165
 * in conjunction with the {@see 'wp_redirect'} and {@see 'wp_redirect_location'} hooks:
1166
 *
1167
 *     if ( wp_redirect( $url ) ) {
1168
 *         exit;
1169
 *     }
1170
 *
1171
 * @since 1.5.1
1172
 *
1173
 * @global bool $is_IIS
1174
 *
1175
 * @param string $location The path to redirect to.
1176
 * @param int    $status   Status code to use.
1177
 * @return bool False if $location is not provided, true otherwise.
1178
 */
1179
function wp_redirect($location, $status = 302) {
1180
	global $is_IIS;
1181
1182
	/**
1183
	 * Filters the redirect location.
1184
	 *
1185
	 * @since 2.1.0
1186
	 *
1187
	 * @param string $location The path to redirect to.
1188
	 * @param int    $status   Status code to use.
1189
	 */
1190
	$location = apply_filters( 'wp_redirect', $location, $status );
1191
1192
	/**
1193
	 * Filters the redirect status code.
1194
	 *
1195
	 * @since 2.3.0
1196
	 *
1197
	 * @param int    $status   Status code to use.
1198
	 * @param string $location The path to redirect to.
1199
	 */
1200
	$status = apply_filters( 'wp_redirect_status', $status, $location );
1201
1202
	if ( ! $location )
1203
		return false;
1204
1205
	$location = wp_sanitize_redirect($location);
1206
1207
	if ( !$is_IIS && PHP_SAPI != 'cgi-fcgi' )
1208
		status_header($status); // This causes problems on IIS and some FastCGI setups
1209
1210
	header("Location: $location", true, $status);
1211
1212
	return true;
1213
}
1214
endif;
1215
1216
if ( !function_exists('wp_sanitize_redirect') ) :
1217
/**
1218
 * Sanitizes a URL for use in a redirect.
1219
 *
1220
 * @since 2.3.0
1221
 *
1222
 * @param string $location The path to redirect to.
1223
 * @return string Redirect-sanitized URL.
1224
 **/
1225
function wp_sanitize_redirect($location) {
1226
	$regex = '/
1227
		(
1228
			(?: [\xC2-\xDF][\x80-\xBF]        # double-byte sequences   110xxxxx 10xxxxxx
1229
			|   \xE0[\xA0-\xBF][\x80-\xBF]    # triple-byte sequences   1110xxxx 10xxxxxx * 2
1230
			|   [\xE1-\xEC][\x80-\xBF]{2}
1231
			|   \xED[\x80-\x9F][\x80-\xBF]
1232
			|   [\xEE-\xEF][\x80-\xBF]{2}
1233
			|   \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences   11110xxx 10xxxxxx * 3
1234
			|   [\xF1-\xF3][\x80-\xBF]{3}
1235
			|   \xF4[\x80-\x8F][\x80-\xBF]{2}
1236
		){1,40}                              # ...one or more times
1237
		)/x';
1238
	$location = preg_replace_callback( $regex, '_wp_sanitize_utf8_in_redirect', $location );
1239
	$location = preg_replace('|[^a-z0-9-~+_.?#=&;,/:%!*\[\]()@]|i', '', $location);
1240
	$location = wp_kses_no_null($location);
1241
1242
	// remove %0d and %0a from location
1243
	$strip = array('%0d', '%0a', '%0D', '%0A');
1244
	return _deep_replace( $strip, $location );
1245
}
1246
1247
/**
1248
 * URL encode UTF-8 characters in a URL.
1249
 *
1250
 * @ignore
1251
 * @since 4.2.0
1252
 * @access private
1253
 *
1254
 * @see wp_sanitize_redirect()
1255
 *
1256
 * @param array $matches RegEx matches against the redirect location.
1257
 * @return string URL-encoded version of the first RegEx match.
1258
 */
1259
function _wp_sanitize_utf8_in_redirect( $matches ) {
1260
	return urlencode( $matches[0] );
1261
}
1262
endif;
1263
1264
if ( !function_exists('wp_safe_redirect') ) :
1265
/**
1266
 * Performs a safe (local) redirect, using wp_redirect().
1267
 *
1268
 * Checks whether the $location is using an allowed host, if it has an absolute
1269
 * path. A plugin can therefore set or remove allowed host(s) to or from the
1270
 * list.
1271
 *
1272
 * If the host is not allowed, then the redirect defaults to wp-admin on the siteurl
1273
 * instead. This prevents malicious redirects which redirect to another host,
1274
 * but only used in a few places.
1275
 *
1276
 * @since 2.3.0
1277
 *
1278
 * @param string $location The path to redirect to.
1279
 * @param int    $status   Status code to use.
1280
 */
1281
function wp_safe_redirect($location, $status = 302) {
1282
1283
	// Need to look at the URL the way it will end up in wp_redirect()
1284
	$location = wp_sanitize_redirect($location);
1285
1286
	/**
1287
	 * Filters the redirect fallback URL for when the provided redirect is not safe (local).
1288
	 *
1289
	 * @since 4.3.0
1290
	 *
1291
	 * @param string $fallback_url The fallback URL to use by default.
1292
	 * @param int    $status       The redirect status.
1293
	 */
1294
	$location = wp_validate_redirect( $location, apply_filters( 'wp_safe_redirect_fallback', admin_url(), $status ) );
1295
1296
	wp_redirect($location, $status);
1297
}
1298
endif;
1299
1300
if ( !function_exists('wp_validate_redirect') ) :
1301
/**
1302
 * Validates a URL for use in a redirect.
1303
 *
1304
 * Checks whether the $location is using an allowed host, if it has an absolute
1305
 * path. A plugin can therefore set or remove allowed host(s) to or from the
1306
 * list.
1307
 *
1308
 * If the host is not allowed, then the redirect is to $default supplied
1309
 *
1310
 * @since 2.8.1
1311
 *
1312
 * @param string $location The redirect to validate
1313
 * @param string $default  The value to return if $location is not allowed
1314
 * @return string redirect-sanitized URL
1315
 **/
1316
function wp_validate_redirect($location, $default = '') {
1317
	$location = trim( $location, " \t\n\r\0\x08\x0B" );
1318
	// browsers will assume 'http' is your protocol, and will obey a redirect to a URL starting with '//'
1319
	if ( substr($location, 0, 2) == '//' )
1320
		$location = 'http:' . $location;
1321
1322
	// In php 5 parse_url may fail if the URL query part contains http://, bug #38143
1323
	$test = ( $cut = strpos($location, '?') ) ? substr( $location, 0, $cut ) : $location;
1324
1325
	// @-operator is used to prevent possible warnings in PHP < 5.3.3.
1326
	$lp = @parse_url($test);
1327
1328
	// Give up if malformed URL
1329
	if ( false === $lp )
1330
		return $default;
1331
1332
	// Allow only http and https schemes. No data:, etc.
1333
	if ( isset($lp['scheme']) && !('http' == $lp['scheme'] || 'https' == $lp['scheme']) )
1334
		return $default;
1335
1336
	// Reject if certain components are set but host is not. This catches urls like https:host.com for which parse_url does not set the host field.
1337
	if ( ! isset( $lp['host'] ) && ( isset( $lp['scheme'] ) || isset( $lp['user'] ) || isset( $lp['pass'] ) || isset( $lp['port'] ) ) ) {
1338
		return $default;
1339
	}
1340
1341
	// Reject malformed components parse_url() can return on odd inputs.
1342
	foreach ( array( 'user', 'pass', 'host' ) as $component ) {
1343
		if ( isset( $lp[ $component ] ) && strpbrk( $lp[ $component ], ':/?#@' ) ) {
1344
			return $default;
1345
		}
1346
	}
1347
1348
	$wpp = parse_url(home_url());
1349
1350
	/**
1351
	 * Filters the whitelist of hosts to redirect to.
1352
	 *
1353
	 * @since 2.3.0
1354
	 *
1355
	 * @param array       $hosts An array of allowed hosts.
1356
	 * @param bool|string $host  The parsed host; empty if not isset.
1357
	 */
1358
	$allowed_hosts = (array) apply_filters( 'allowed_redirect_hosts', array($wpp['host']), isset($lp['host']) ? $lp['host'] : '' );
1359
1360
	if ( isset($lp['host']) && ( !in_array($lp['host'], $allowed_hosts) && $lp['host'] != strtolower($wpp['host'])) )
1361
		$location = $default;
1362
1363
	return $location;
1364
}
1365
endif;
1366
1367
if ( ! function_exists('wp_notify_postauthor') ) :
1368
/**
1369
 * Notify an author (and/or others) of a comment/trackback/pingback on a post.
1370
 *
1371
 * @since 1.0.0
1372
 *
1373
 * @param int|WP_Comment  $comment_id Comment ID or WP_Comment object.
1374
 * @param string          $deprecated Not used
1375
 * @return bool True on completion. False if no email addresses were specified.
1376
 */
1377
function wp_notify_postauthor( $comment_id, $deprecated = null ) {
1378
	if ( null !== $deprecated ) {
1379
		_deprecated_argument( __FUNCTION__, '3.8.0' );
1380
	}
1381
1382
	$comment = get_comment( $comment_id );
1383
	if ( empty( $comment ) || empty( $comment->comment_post_ID ) )
1384
		return false;
1385
1386
	$post    = get_post( $comment->comment_post_ID );
1387
	$author  = get_userdata( $post->post_author );
1388
1389
	// Who to notify? By default, just the post author, but others can be added.
1390
	$emails = array();
1391
	if ( $author ) {
1392
		$emails[] = $author->user_email;
1393
	}
1394
1395
	/**
1396
	 * Filters the list of email addresses to receive a comment notification.
1397
	 *
1398
	 * By default, only post authors are notified of comments. This filter allows
1399
	 * others to be added.
1400
	 *
1401
	 * @since 3.7.0
1402
	 *
1403
	 * @param array $emails     An array of email addresses to receive a comment notification.
1404
	 * @param int   $comment_id The comment ID.
1405
	 */
1406
	$emails = apply_filters( 'comment_notification_recipients', $emails, $comment->comment_ID );
1407
	$emails = array_filter( $emails );
1408
1409
	// If there are no addresses to send the comment to, bail.
1410
	if ( ! count( $emails ) ) {
1411
		return false;
1412
	}
1413
1414
	// Facilitate unsetting below without knowing the keys.
1415
	$emails = array_flip( $emails );
1416
1417
	/**
1418
	 * Filters whether to notify comment authors of their comments on their own posts.
1419
	 *
1420
	 * By default, comment authors aren't notified of their comments on their own
1421
	 * posts. This filter allows you to override that.
1422
	 *
1423
	 * @since 3.8.0
1424
	 *
1425
	 * @param bool $notify     Whether to notify the post author of their own comment.
1426
	 *                         Default false.
1427
	 * @param int  $comment_id The comment ID.
1428
	 */
1429
	$notify_author = apply_filters( 'comment_notification_notify_author', false, $comment->comment_ID );
1430
1431
	// The comment was left by the author
1432
	if ( $author && ! $notify_author && $comment->user_id == $post->post_author ) {
1433
		unset( $emails[ $author->user_email ] );
1434
	}
1435
1436
	// The author moderated a comment on their own post
1437
	if ( $author && ! $notify_author && $post->post_author == get_current_user_id() ) {
1438
		unset( $emails[ $author->user_email ] );
1439
	}
1440
1441
	// The post author is no longer a member of the blog
1442
	if ( $author && ! $notify_author && ! user_can( $post->post_author, 'read_post', $post->ID ) ) {
1443
		unset( $emails[ $author->user_email ] );
1444
	}
1445
1446
	// If there's no email to send the comment to, bail, otherwise flip array back around for use below
1447
	if ( ! count( $emails ) ) {
1448
		return false;
1449
	} else {
1450
		$emails = array_flip( $emails );
1451
	}
1452
1453
	$switched_locale = switch_to_locale( get_locale() );
1454
1455
	$comment_author_domain = @gethostbyaddr($comment->comment_author_IP);
1456
1457
	// The blogname option is escaped with esc_html on the way into the database in sanitize_option
1458
	// we want to reverse this for the plain text arena of emails.
1459
	$blogname = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
1460
	$comment_content = wp_specialchars_decode( $comment->comment_content );
1461
1462
	switch ( $comment->comment_type ) {
1463 View Code Duplication
		case 'trackback':
1464
			/* translators: 1: Post title */
1465
			$notify_message  = sprintf( __( 'New trackback on your post "%s"' ), $post->post_title ) . "\r\n";
1466
			/* translators: 1: Trackback/pingback website name, 2: website IP, 3: website hostname */
1467
			$notify_message .= sprintf( __('Website: %1$s (IP: %2$s, %3$s)'), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
1468
			$notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
1469
			$notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
1470
			$notify_message .= __( 'You can see all trackbacks on this post here:' ) . "\r\n";
1471
			/* translators: 1: blog name, 2: post title */
1472
			$subject = sprintf( __('[%1$s] Trackback: "%2$s"'), $blogname, $post->post_title );
1473
			break;
1474 View Code Duplication
		case 'pingback':
1475
			/* translators: 1: Post title */
1476
			$notify_message  = sprintf( __( 'New pingback on your post "%s"' ), $post->post_title ) . "\r\n";
1477
			/* translators: 1: Trackback/pingback website name, 2: website IP, 3: website hostname */
1478
			$notify_message .= sprintf( __('Website: %1$s (IP: %2$s, %3$s)'), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
1479
			$notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
1480
			$notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
1481
			$notify_message .= __( 'You can see all pingbacks on this post here:' ) . "\r\n";
1482
			/* translators: 1: blog name, 2: post title */
1483
			$subject = sprintf( __('[%1$s] Pingback: "%2$s"'), $blogname, $post->post_title );
1484
			break;
1485
		default: // Comments
1486
			$notify_message  = sprintf( __( 'New comment on your post "%s"' ), $post->post_title ) . "\r\n";
1487
			/* translators: 1: comment author, 2: author IP, 3: author domain */
1488
			$notify_message .= sprintf( __( 'Author: %1$s (IP: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
1489
			$notify_message .= sprintf( __( 'Email: %s' ), $comment->comment_author_email ) . "\r\n";
1490
			$notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
1491
			$notify_message .= sprintf( __('Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
1492
			$notify_message .= __( 'You can see all comments on this post here:' ) . "\r\n";
1493
			/* translators: 1: blog name, 2: post title */
1494
			$subject = sprintf( __('[%1$s] Comment: "%2$s"'), $blogname, $post->post_title );
1495
			break;
1496
	}
1497
	$notify_message .= get_permalink($comment->comment_post_ID) . "#comments\r\n\r\n";
1498
	$notify_message .= sprintf( __('Permalink: %s'), get_comment_link( $comment ) ) . "\r\n";
1499
1500
	if ( user_can( $post->post_author, 'edit_comment', $comment->comment_ID ) ) {
1501 View Code Duplication
		if ( EMPTY_TRASH_DAYS ) {
1502
			$notify_message .= sprintf( __( 'Trash it: %s' ), admin_url( "comment.php?action=trash&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n";
1503
		} else {
1504
			$notify_message .= sprintf( __( 'Delete it: %s' ), admin_url( "comment.php?action=delete&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n";
1505
		}
1506
		$notify_message .= sprintf( __( 'Spam it: %s' ), admin_url( "comment.php?action=spam&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n";
1507
	}
1508
1509
	$wp_email = 'wordpress@' . preg_replace('#^www\.#', '', strtolower($_SERVER['SERVER_NAME']));
1510
1511
	if ( '' == $comment->comment_author ) {
1512
		$from = "From: \"$blogname\" <$wp_email>";
1513
		if ( '' != $comment->comment_author_email )
1514
			$reply_to = "Reply-To: $comment->comment_author_email";
1515
	} else {
1516
		$from = "From: \"$comment->comment_author\" <$wp_email>";
1517
		if ( '' != $comment->comment_author_email )
1518
			$reply_to = "Reply-To: \"$comment->comment_author_email\" <$comment->comment_author_email>";
1519
	}
1520
1521
	$message_headers = "$from\n"
1522
		. "Content-Type: text/plain; charset=\"" . get_option('blog_charset') . "\"\n";
1523
1524
	if ( isset($reply_to) )
1525
		$message_headers .= $reply_to . "\n";
1526
1527
	/**
1528
	 * Filters the comment notification email text.
1529
	 *
1530
	 * @since 1.5.2
1531
	 *
1532
	 * @param string $notify_message The comment notification email text.
1533
	 * @param int    $comment_id     Comment ID.
1534
	 */
1535
	$notify_message = apply_filters( 'comment_notification_text', $notify_message, $comment->comment_ID );
1536
1537
	/**
1538
	 * Filters the comment notification email subject.
1539
	 *
1540
	 * @since 1.5.2
1541
	 *
1542
	 * @param string $subject    The comment notification email subject.
1543
	 * @param int    $comment_id Comment ID.
1544
	 */
1545
	$subject = apply_filters( 'comment_notification_subject', $subject, $comment->comment_ID );
1546
1547
	/**
1548
	 * Filters the comment notification email headers.
1549
	 *
1550
	 * @since 1.5.2
1551
	 *
1552
	 * @param string $message_headers Headers for the comment notification email.
1553
	 * @param int    $comment_id      Comment ID.
1554
	 */
1555
	$message_headers = apply_filters( 'comment_notification_headers', $message_headers, $comment->comment_ID );
1556
1557
	foreach ( $emails as $email ) {
1558
		@wp_mail( $email, wp_specialchars_decode( $subject ), $notify_message, $message_headers );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1559
	}
1560
1561
	if ( $switched_locale ) {
1562
		restore_previous_locale();
1563
	}
1564
1565
	return true;
1566
}
1567
endif;
1568
1569
if ( !function_exists('wp_notify_moderator') ) :
1570
/**
1571
 * Notifies the moderator of the site about a new comment that is awaiting approval.
1572
 *
1573
 * @since 1.0.0
1574
 *
1575
 * @global wpdb $wpdb WordPress database abstraction object.
1576
 *
1577
 * Uses the {@see 'notify_moderator'} filter to determine whether the site moderator
1578
 * should be notified, overriding the site setting.
1579
 *
1580
 * @param int $comment_id Comment ID.
1581
 * @return true Always returns true.
0 ignored issues
show
Should the return type not be boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
1582
 */
1583
function wp_notify_moderator($comment_id) {
1584
	global $wpdb;
1585
1586
	$maybe_notify = get_option( 'moderation_notify' );
1587
1588
	/**
1589
	 * Filters whether to send the site moderator email notifications, overriding the site setting.
1590
	 *
1591
	 * @since 4.4.0
1592
	 *
1593
	 * @param bool $maybe_notify Whether to notify blog moderator.
1594
	 * @param int  $comment_ID   The id of the comment for the notification.
1595
	 */
1596
	$maybe_notify = apply_filters( 'notify_moderator', $maybe_notify, $comment_id );
1597
1598
	if ( ! $maybe_notify ) {
1599
		return true;
1600
	}
1601
1602
	$comment = get_comment($comment_id);
1603
	$post = get_post($comment->comment_post_ID);
1604
	$user = get_userdata( $post->post_author );
1605
	// Send to the administration and to the post author if the author can modify the comment.
1606
	$emails = array( get_option( 'admin_email' ) );
1607
	if ( $user && user_can( $user->ID, 'edit_comment', $comment_id ) && ! empty( $user->user_email ) ) {
1608
		if ( 0 !== strcasecmp( $user->user_email, get_option( 'admin_email' ) ) )
1609
			$emails[] = $user->user_email;
1610
	}
1611
1612
	$switched_locale = switch_to_locale( get_locale() );
1613
1614
	$comment_author_domain = @gethostbyaddr($comment->comment_author_IP);
1615
	$comments_waiting = $wpdb->get_var("SELECT count(comment_ID) FROM $wpdb->comments WHERE comment_approved = '0'");
1616
1617
	// The blogname option is escaped with esc_html on the way into the database in sanitize_option
1618
	// we want to reverse this for the plain text arena of emails.
1619
	$blogname = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
1620
	$comment_content = wp_specialchars_decode( $comment->comment_content );
1621
1622
	switch ( $comment->comment_type ) {
1623 View Code Duplication
		case 'trackback':
1624
			/* translators: 1: Post title */
1625
			$notify_message  = sprintf( __('A new trackback on the post "%s" is waiting for your approval'), $post->post_title ) . "\r\n";
1626
			$notify_message .= get_permalink($comment->comment_post_ID) . "\r\n\r\n";
1627
			/* translators: 1: Trackback/pingback website name, 2: website IP, 3: website hostname */
1628
			$notify_message .= sprintf( __( 'Website: %1$s (IP: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
1629
			/* translators: 1: Trackback/pingback/comment author URL */
1630
			$notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
1631
			$notify_message .= __('Trackback excerpt: ') . "\r\n" . $comment_content . "\r\n\r\n";
1632
			break;
1633 View Code Duplication
		case 'pingback':
1634
			/* translators: 1: Post title */
1635
			$notify_message  = sprintf( __('A new pingback on the post "%s" is waiting for your approval'), $post->post_title ) . "\r\n";
1636
			$notify_message .= get_permalink($comment->comment_post_ID) . "\r\n\r\n";
1637
			/* translators: 1: Trackback/pingback website name, 2: website IP, 3: website hostname */
1638
			$notify_message .= sprintf( __( 'Website: %1$s (IP: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
1639
			/* translators: 1: Trackback/pingback/comment author URL */
1640
			$notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
1641
			$notify_message .= __('Pingback excerpt: ') . "\r\n" . $comment_content . "\r\n\r\n";
1642
			break;
1643 View Code Duplication
		default: // Comments
1644
			/* translators: 1: Post title */
1645
			$notify_message  = sprintf( __('A new comment on the post "%s" is waiting for your approval'), $post->post_title ) . "\r\n";
1646
			$notify_message .= get_permalink($comment->comment_post_ID) . "\r\n\r\n";
1647
			/* translators: 1: Comment author name, 2: comment author's IP, 3: comment author IP's hostname */
1648
			$notify_message .= sprintf( __( 'Author: %1$s (IP: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
1649
			/* translators: 1: Comment author URL */
1650
			$notify_message .= sprintf( __( 'Email: %s' ), $comment->comment_author_email ) . "\r\n";
1651
			/* translators: 1: Trackback/pingback/comment author URL */
1652
			$notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
1653
			/* translators: 1: Comment text */
1654
			$notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
1655
			break;
1656
	}
1657
1658
	/* translators: Comment moderation. 1: Comment action URL */
1659
	$notify_message .= sprintf( __( 'Approve it: %s' ), admin_url( "comment.php?action=approve&c={$comment_id}#wpbody-content" ) ) . "\r\n";
1660
1661 View Code Duplication
	if ( EMPTY_TRASH_DAYS ) {
1662
		/* translators: Comment moderation. 1: Comment action URL */
1663
		$notify_message .= sprintf( __( 'Trash it: %s' ), admin_url( "comment.php?action=trash&c={$comment_id}#wpbody-content" ) ) . "\r\n";
1664
	} else {
1665
		/* translators: Comment moderation. 1: Comment action URL */
1666
		$notify_message .= sprintf( __( 'Delete it: %s' ), admin_url( "comment.php?action=delete&c={$comment_id}#wpbody-content" ) ) . "\r\n";
1667
	}
1668
1669
	/* translators: Comment moderation. 1: Comment action URL */
1670
	$notify_message .= sprintf( __( 'Spam it: %s' ), admin_url( "comment.php?action=spam&c={$comment_id}#wpbody-content" ) ) . "\r\n";
1671
1672
	/* translators: Comment moderation. 1: Number of comments awaiting approval */
1673
	$notify_message .= sprintf( _n('Currently %s comment is waiting for approval. Please visit the moderation panel:',
1674
 		'Currently %s comments are waiting for approval. Please visit the moderation panel:', $comments_waiting), number_format_i18n($comments_waiting) ) . "\r\n";
1675
	$notify_message .= admin_url( "edit-comments.php?comment_status=moderated#wpbody-content" ) . "\r\n";
1676
1677
	/* translators: Comment moderation notification email subject. 1: Site name, 2: Post title */
1678
	$subject = sprintf( __('[%1$s] Please moderate: "%2$s"'), $blogname, $post->post_title );
1679
	$message_headers = '';
1680
1681
	/**
1682
	 * Filters the list of recipients for comment moderation emails.
1683
	 *
1684
	 * @since 3.7.0
1685
	 *
1686
	 * @param array $emails     List of email addresses to notify for comment moderation.
1687
	 * @param int   $comment_id Comment ID.
1688
	 */
1689
	$emails = apply_filters( 'comment_moderation_recipients', $emails, $comment_id );
1690
1691
	/**
1692
	 * Filters the comment moderation email text.
1693
	 *
1694
	 * @since 1.5.2
1695
	 *
1696
	 * @param string $notify_message Text of the comment moderation email.
1697
	 * @param int    $comment_id     Comment ID.
1698
	 */
1699
	$notify_message = apply_filters( 'comment_moderation_text', $notify_message, $comment_id );
1700
1701
	/**
1702
	 * Filters the comment moderation email subject.
1703
	 *
1704
	 * @since 1.5.2
1705
	 *
1706
	 * @param string $subject    Subject of the comment moderation email.
1707
	 * @param int    $comment_id Comment ID.
1708
	 */
1709
	$subject = apply_filters( 'comment_moderation_subject', $subject, $comment_id );
1710
1711
	/**
1712
	 * Filters the comment moderation email headers.
1713
	 *
1714
	 * @since 2.8.0
1715
	 *
1716
	 * @param string $message_headers Headers for the comment moderation email.
1717
	 * @param int    $comment_id      Comment ID.
1718
	 */
1719
	$message_headers = apply_filters( 'comment_moderation_headers', $message_headers, $comment_id );
1720
1721
	foreach ( $emails as $email ) {
1722
		@wp_mail( $email, wp_specialchars_decode( $subject ), $notify_message, $message_headers );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1723
	}
1724
1725
	if ( $switched_locale ) {
1726
		restore_previous_locale();
1727
	}
1728
1729
	return true;
1730
}
1731
endif;
1732
1733
if ( !function_exists('wp_password_change_notification') ) :
1734
/**
1735
 * Notify the blog admin of a user changing password, normally via email.
1736
 *
1737
 * @since 2.7.0
1738
 *
1739
 * @param WP_User $user User object.
1740
 */
1741
function wp_password_change_notification( $user ) {
1742
	// send a copy of password change notification to the admin
1743
	// but check to see if it's the admin whose password we're changing, and skip this
1744
	if ( 0 !== strcasecmp( $user->user_email, get_option( 'admin_email' ) ) ) {
1745
		/* translators: %s: user name */
1746
		$message = sprintf( __( 'Password changed for user: %s' ), $user->user_login ) . "\r\n";
1747
		// The blogname option is escaped with esc_html on the way into the database in sanitize_option
1748
		// we want to reverse this for the plain text arena of emails.
1749
		$blogname = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
1750
		/* translators: %s: site title */
1751
		wp_mail( get_option( 'admin_email' ), sprintf( __( '[%s] Password Changed' ), $blogname ), $message );
1752
	}
1753
}
1754
endif;
1755
1756
if ( !function_exists('wp_new_user_notification') ) :
1757
/**
1758
 * Email login credentials to a newly-registered user.
1759
 *
1760
 * A new user registration notification is also sent to admin email.
1761
 *
1762
 * @since 2.0.0
1763
 * @since 4.3.0 The `$plaintext_pass` parameter was changed to `$notify`.
1764
 * @since 4.3.1 The `$plaintext_pass` parameter was deprecated. `$notify` added as a third parameter.
1765
 * @since 4.6.0 The `$notify` parameter accepts 'user' for sending notification only to the user created.
1766
 *
1767
 * @global wpdb         $wpdb      WordPress database object for queries.
1768
 * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
1769
 *
1770
 * @param int    $user_id    User ID.
1771
 * @param null   $deprecated Not used (argument deprecated).
1772
 * @param string $notify     Optional. Type of notification that should happen. Accepts 'admin' or an empty
1773
 *                           string (admin only), 'user', or 'both' (admin and user). Default empty.
1774
 */
1775
function wp_new_user_notification( $user_id, $deprecated = null, $notify = '' ) {
1776
	if ( $deprecated !== null ) {
1777
		_deprecated_argument( __FUNCTION__, '4.3.1' );
1778
	}
1779
1780
	global $wpdb, $wp_hasher;
1781
	$user = get_userdata( $user_id );
1782
1783
	// The blogname option is escaped with esc_html on the way into the database in sanitize_option
1784
	// we want to reverse this for the plain text arena of emails.
1785
	$blogname = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
1786
1787
	if ( 'user' !== $notify ) {
1788
		$switched_locale = switch_to_locale( get_locale() );
1789
		$message  = sprintf( __( 'New user registration on your site %s:' ), $blogname ) . "\r\n\r\n";
1790
		$message .= sprintf( __( 'Username: %s' ), $user->user_login ) . "\r\n\r\n";
1791
		$message .= sprintf( __( 'Email: %s' ), $user->user_email ) . "\r\n";
1792
1793
		@wp_mail( get_option( 'admin_email' ), sprintf( __( '[%s] New User Registration' ), $blogname ), $message );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1794
1795
		if ( $switched_locale ) {
1796
			restore_previous_locale();
1797
		}
1798
	}
1799
1800
	// `$deprecated was pre-4.3 `$plaintext_pass`. An empty `$plaintext_pass` didn't sent a user notification.
1801
	if ( 'admin' === $notify || ( empty( $deprecated ) && empty( $notify ) ) ) {
1802
		return;
1803
	}
1804
1805
	// Generate something random for a password reset key.
1806
	$key = wp_generate_password( 20, false );
1807
1808
	/** This action is documented in wp-login.php */
1809
	do_action( 'retrieve_password_key', $user->user_login, $key );
1810
1811
	// Now insert the key, hashed, into the DB.
1812 View Code Duplication
	if ( empty( $wp_hasher ) ) {
1813
		require_once ABSPATH . WPINC . '/class-phpass.php';
1814
		$wp_hasher = new PasswordHash( 8, true );
1815
	}
1816
	$hashed = time() . ':' . $wp_hasher->HashPassword( $key );
1817
	$wpdb->update( $wpdb->users, array( 'user_activation_key' => $hashed ), array( 'user_login' => $user->user_login ) );
1818
1819
	$switched_locale = switch_to_locale( get_user_locale( $user ) );
1820
1821
	$message = sprintf(__('Username: %s'), $user->user_login) . "\r\n\r\n";
1822
	$message .= __('To set your password, visit the following address:') . "\r\n\r\n";
1823
	$message .= '<' . network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user->user_login), 'login') . ">\r\n\r\n";
1824
1825
	$message .= wp_login_url() . "\r\n";
1826
1827
	wp_mail($user->user_email, sprintf(__('[%s] Your username and password info'), $blogname), $message);
1828
1829
	if ( $switched_locale ) {
1830
		restore_previous_locale();
1831
	}
1832
}
1833
endif;
1834
1835
if ( !function_exists('wp_nonce_tick') ) :
1836
/**
1837
 * Get the time-dependent variable for nonce creation.
1838
 *
1839
 * A nonce has a lifespan of two ticks. Nonces in their second tick may be
1840
 * updated, e.g. by autosave.
1841
 *
1842
 * @since 2.5.0
1843
 *
1844
 * @return float Float value rounded up to the next highest integer.
1845
 */
1846
function wp_nonce_tick() {
1847
	/**
1848
	 * Filters the lifespan of nonces in seconds.
1849
	 *
1850
	 * @since 2.5.0
1851
	 *
1852
	 * @param int $lifespan Lifespan of nonces in seconds. Default 86,400 seconds, or one day.
1853
	 */
1854
	$nonce_life = apply_filters( 'nonce_life', DAY_IN_SECONDS );
1855
1856
	return ceil(time() / ( $nonce_life / 2 ));
1857
}
1858
endif;
1859
1860
if ( !function_exists('wp_verify_nonce') ) :
1861
/**
1862
 * Verify that correct nonce was used with time limit.
1863
 *
1864
 * The user is given an amount of time to use the token, so therefore, since the
1865
 * UID and $action remain the same, the independent variable is the time.
1866
 *
1867
 * @since 2.0.3
1868
 *
1869
 * @param string     $nonce  Nonce that was used in the form to verify
1870
 * @param string|int $action Should give context to what is taking place and be the same when nonce was created.
0 ignored issues
show
Consider making the type for parameter $action a bit more specific; maybe use integer.
Loading history...
1871
 * @return false|int False if the nonce is invalid, 1 if the nonce is valid and generated between
1872
 *                   0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago.
1873
 */
1874
function wp_verify_nonce( $nonce, $action = -1 ) {
1875
	$nonce = (string) $nonce;
1876
	$user = wp_get_current_user();
1877
	$uid = (int) $user->ID;
1878
	if ( ! $uid ) {
1879
		/**
1880
		 * Filters whether the user who generated the nonce is logged out.
1881
		 *
1882
		 * @since 3.5.0
1883
		 *
1884
		 * @param int    $uid    ID of the nonce-owning user.
1885
		 * @param string $action The nonce action.
1886
		 */
1887
		$uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
1888
	}
1889
1890
	if ( empty( $nonce ) ) {
1891
		return false;
1892
	}
1893
1894
	$token = wp_get_session_token();
1895
	$i = wp_nonce_tick();
1896
1897
	// Nonce generated 0-12 hours ago
1898
	$expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce'), -12, 10 );
1899
	if ( hash_equals( $expected, $nonce ) ) {
1900
		return 1;
1901
	}
1902
1903
	// Nonce generated 12-24 hours ago
1904
	$expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
1905
	if ( hash_equals( $expected, $nonce ) ) {
1906
		return 2;
1907
	}
1908
1909
	/**
1910
	 * Fires when nonce verification fails.
1911
	 *
1912
	 * @since 4.4.0
1913
	 *
1914
	 * @param string     $nonce  The invalid nonce.
1915
	 * @param string|int $action The nonce action.
1916
	 * @param WP_User    $user   The current user object.
1917
	 * @param string     $token  The user's session token.
1918
	 */
1919
	do_action( 'wp_verify_nonce_failed', $nonce, $action, $user, $token );
1920
1921
	// Invalid nonce
1922
	return false;
1923
}
1924
endif;
1925
1926
if ( !function_exists('wp_create_nonce') ) :
1927
/**
1928
 * Creates a cryptographic token tied to a specific action, user, user session,
1929
 * and window of time.
1930
 *
1931
 * @since 2.0.3
1932
 * @since 4.0.0 Session tokens were integrated with nonce creation
1933
 *
1934
 * @param string|int $action Scalar value to add context to the nonce.
0 ignored issues
show
Consider making the type for parameter $action a bit more specific; maybe use integer.
Loading history...
1935
 * @return string The token.
1936
 */
1937
function wp_create_nonce($action = -1) {
1938
	$user = wp_get_current_user();
1939
	$uid = (int) $user->ID;
1940
	if ( ! $uid ) {
1941
		/** This filter is documented in wp-includes/pluggable.php */
1942
		$uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
1943
	}
1944
1945
	$token = wp_get_session_token();
1946
	$i = wp_nonce_tick();
1947
1948
	return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
1949
}
1950
endif;
1951
1952
if ( !function_exists('wp_salt') ) :
1953
/**
1954
 * Get salt to add to hashes.
1955
 *
1956
 * Salts are created using secret keys. Secret keys are located in two places:
1957
 * in the database and in the wp-config.php file. The secret key in the database
1958
 * is randomly generated and will be appended to the secret keys in wp-config.php.
1959
 *
1960
 * The secret keys in wp-config.php should be updated to strong, random keys to maximize
1961
 * security. Below is an example of how the secret key constants are defined.
1962
 * Do not paste this example directly into wp-config.php. Instead, have a
1963
 * {@link https://api.wordpress.org/secret-key/1.1/salt/ secret key created} just
1964
 * for you.
1965
 *
1966
 *     define('AUTH_KEY',         ' Xakm<o xQy rw4EMsLKM-?!T+,PFF})H4lzcW57AF0U@N@< >M%G4Yt>f`z]MON');
1967
 *     define('SECURE_AUTH_KEY',  'LzJ}op]mr|6+![P}Ak:uNdJCJZd>(Hx.-Mh#Tz)pCIU#uGEnfFz|f ;;eU%/U^O~');
1968
 *     define('LOGGED_IN_KEY',    '|i|Ux`9<p-h$aFf(qnT:sDO:D1P^wZ$$/Ra@miTJi9G;ddp_<q}6H1)o|a +&JCM');
1969
 *     define('NONCE_KEY',        '%:R{[P|,s.KuMltH5}cI;/k<Gx~j!f0I)m_sIyu+&NJZ)-iO>z7X>QYR0Z_XnZ@|');
1970
 *     define('AUTH_SALT',        'eZyT)-Naw]F8CwA*VaW#q*|.)g@o}||wf~@C-YSt}(dh_r6EbI#A,y|nU2{B#JBW');
1971
 *     define('SECURE_AUTH_SALT', '!=oLUTXh,QW=H `}`L|9/^4-3 STz},T(w}W<I`.JjPi)<Bmf1v,HpGe}T1:Xt7n');
1972
 *     define('LOGGED_IN_SALT',   '+XSqHc;@Q*K_b|Z?NC[3H!!EONbh.n<+=uKR:>*c(u`g~EJBf#8u#R{mUEZrozmm');
1973
 *     define('NONCE_SALT',       'h`GXHhD>SLWVfg1(1(N{;.V!MoE(SfbA_ksP@&`+AycHcAV$+?@3q+rxV{%^VyKT');
1974
 *
1975
 * Salting passwords helps against tools which has stored hashed values of
1976
 * common dictionary strings. The added values makes it harder to crack.
1977
 *
1978
 * @since 2.5.0
1979
 *
1980
 * @link https://api.wordpress.org/secret-key/1.1/salt/ Create secrets for wp-config.php
1981
 *
1982
 * @staticvar array $cached_salts
1983
 * @staticvar array $duplicated_keys
1984
 *
1985
 * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce)
1986
 * @return string Salt value
1987
 */
1988
function wp_salt( $scheme = 'auth' ) {
1989
	static $cached_salts = array();
1990
	if ( isset( $cached_salts[ $scheme ] ) ) {
1991
		/**
1992
		 * Filters the WordPress salt.
1993
		 *
1994
		 * @since 2.5.0
1995
		 *
1996
		 * @param string $cached_salt Cached salt for the given scheme.
1997
		 * @param string $scheme      Authentication scheme. Values include 'auth',
1998
		 *                            'secure_auth', 'logged_in', and 'nonce'.
1999
		 */
2000
		return apply_filters( 'salt', $cached_salts[ $scheme ], $scheme );
2001
	}
2002
2003
	static $duplicated_keys;
2004
	if ( null === $duplicated_keys ) {
2005
		$duplicated_keys = array( 'put your unique phrase here' => true );
2006
		foreach ( array( 'AUTH', 'SECURE_AUTH', 'LOGGED_IN', 'NONCE', 'SECRET' ) as $first ) {
2007
			foreach ( array( 'KEY', 'SALT' ) as $second ) {
2008
				if ( ! defined( "{$first}_{$second}" ) ) {
2009
					continue;
2010
				}
2011
				$value = constant( "{$first}_{$second}" );
2012
				$duplicated_keys[ $value ] = isset( $duplicated_keys[ $value ] );
2013
			}
2014
		}
2015
	}
2016
2017
	$values = array(
2018
		'key' => '',
2019
		'salt' => ''
2020
	);
2021
	if ( defined( 'SECRET_KEY' ) && SECRET_KEY && empty( $duplicated_keys[ SECRET_KEY ] ) ) {
2022
		$values['key'] = SECRET_KEY;
2023
	}
2024
	if ( 'auth' == $scheme && defined( 'SECRET_SALT' ) && SECRET_SALT && empty( $duplicated_keys[ SECRET_SALT ] ) ) {
2025
		$values['salt'] = SECRET_SALT;
2026
	}
2027
2028
	if ( in_array( $scheme, array( 'auth', 'secure_auth', 'logged_in', 'nonce' ) ) ) {
2029
		foreach ( array( 'key', 'salt' ) as $type ) {
2030
			$const = strtoupper( "{$scheme}_{$type}" );
2031
			if ( defined( $const ) && constant( $const ) && empty( $duplicated_keys[ constant( $const ) ] ) ) {
2032
				$values[ $type ] = constant( $const );
2033
			} elseif ( ! $values[ $type ] ) {
2034
				$values[ $type ] = get_site_option( "{$scheme}_{$type}" );
2035
				if ( ! $values[ $type ] ) {
2036
					$values[ $type ] = wp_generate_password( 64, true, true );
2037
					update_site_option( "{$scheme}_{$type}", $values[ $type ] );
2038
				}
2039
			}
2040
		}
2041
	} else {
2042
		if ( ! $values['key'] ) {
2043
			$values['key'] = get_site_option( 'secret_key' );
2044
			if ( ! $values['key'] ) {
2045
				$values['key'] = wp_generate_password( 64, true, true );
2046
				update_site_option( 'secret_key', $values['key'] );
2047
			}
2048
		}
2049
		$values['salt'] = hash_hmac( 'md5', $scheme, $values['key'] );
2050
	}
2051
2052
	$cached_salts[ $scheme ] = $values['key'] . $values['salt'];
2053
2054
	/** This filter is documented in wp-includes/pluggable.php */
2055
	return apply_filters( 'salt', $cached_salts[ $scheme ], $scheme );
2056
}
2057
endif;
2058
2059
if ( !function_exists('wp_hash') ) :
2060
/**
2061
 * Get hash of given string.
2062
 *
2063
 * @since 2.0.3
2064
 *
2065
 * @param string $data   Plain text to hash
2066
 * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce)
2067
 * @return string Hash of $data
0 ignored issues
show
Should the return type not be string|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2068
 */
2069
function wp_hash($data, $scheme = 'auth') {
2070
	$salt = wp_salt($scheme);
2071
2072
	return hash_hmac('md5', $data, $salt);
2073
}
2074
endif;
2075
2076
if ( !function_exists('wp_hash_password') ) :
2077
/**
2078
 * Create a hash (encrypt) of a plain text password.
2079
 *
2080
 * For integration with other applications, this function can be overwritten to
2081
 * instead use the other package password checking algorithm.
2082
 *
2083
 * @since 2.5.0
2084
 *
2085
 * @global PasswordHash $wp_hasher PHPass object
2086
 *
2087
 * @param string $password Plain text user password to hash
2088
 * @return string The hash string of the password
2089
 */
2090
function wp_hash_password($password) {
2091
	global $wp_hasher;
2092
2093 View Code Duplication
	if ( empty($wp_hasher) ) {
2094
		require_once( ABSPATH . WPINC . '/class-phpass.php');
2095
		// By default, use the portable hash from phpass
2096
		$wp_hasher = new PasswordHash(8, true);
2097
	}
2098
2099
	return $wp_hasher->HashPassword( trim( $password ) );
2100
}
2101
endif;
2102
2103
if ( !function_exists('wp_check_password') ) :
2104
/**
2105
 * Checks the plaintext password against the encrypted Password.
2106
 *
2107
 * Maintains compatibility between old version and the new cookie authentication
2108
 * protocol using PHPass library. The $hash parameter is the encrypted password
2109
 * and the function compares the plain text password when encrypted similarly
2110
 * against the already encrypted password to see if they match.
2111
 *
2112
 * For integration with other applications, this function can be overwritten to
2113
 * instead use the other package password checking algorithm.
2114
 *
2115
 * @since 2.5.0
2116
 *
2117
 * @global PasswordHash $wp_hasher PHPass object used for checking the password
2118
 *	against the $hash + $password
2119
 * @uses PasswordHash::CheckPassword
2120
 *
2121
 * @param string     $password Plaintext user's password
2122
 * @param string     $hash     Hash of the user's password to check against.
2123
 * @param string|int $user_id  Optional. User ID.
2124
 * @return bool False, if the $password does not match the hashed password
2125
 */
2126
function wp_check_password($password, $hash, $user_id = '') {
2127
	global $wp_hasher;
2128
2129
	// If the hash is still md5...
2130
	if ( strlen($hash) <= 32 ) {
2131
		$check = hash_equals( $hash, md5( $password ) );
2132
		if ( $check && $user_id ) {
2133
			// Rehash using new hash.
2134
			wp_set_password($password, $user_id);
2135
			$hash = wp_hash_password($password);
2136
		}
2137
2138
		/**
2139
		 * Filters whether the plaintext password matches the encrypted password.
2140
		 *
2141
		 * @since 2.5.0
2142
		 *
2143
		 * @param bool       $check    Whether the passwords match.
2144
		 * @param string     $password The plaintext password.
2145
		 * @param string     $hash     The hashed password.
2146
		 * @param string|int $user_id  User ID. Can be empty.
2147
		 */
2148
		return apply_filters( 'check_password', $check, $password, $hash, $user_id );
2149
	}
2150
2151
	// If the stored hash is longer than an MD5, presume the
2152
	// new style phpass portable hash.
2153 View Code Duplication
	if ( empty($wp_hasher) ) {
2154
		require_once( ABSPATH . WPINC . '/class-phpass.php');
2155
		// By default, use the portable hash from phpass
2156
		$wp_hasher = new PasswordHash(8, true);
2157
	}
2158
2159
	$check = $wp_hasher->CheckPassword($password, $hash);
2160
2161
	/** This filter is documented in wp-includes/pluggable.php */
2162
	return apply_filters( 'check_password', $check, $password, $hash, $user_id );
2163
}
2164
endif;
2165
2166
if ( !function_exists('wp_generate_password') ) :
2167
/**
2168
 * Generates a random password drawn from the defined set of characters.
2169
 *
2170
 * @since 2.5.0
2171
 *
2172
 * @param int  $length              Optional. The length of password to generate. Default 12.
2173
 * @param bool $special_chars       Optional. Whether to include standard special characters.
2174
 *                                  Default true.
2175
 * @param bool $extra_special_chars Optional. Whether to include other special characters.
2176
 *                                  Used when generating secret keys and salts. Default false.
2177
 * @return string The random password.
2178
 */
2179
function wp_generate_password( $length = 12, $special_chars = true, $extra_special_chars = false ) {
2180
	$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
2181
	if ( $special_chars )
2182
		$chars .= '!@#$%^&*()';
2183
	if ( $extra_special_chars )
2184
		$chars .= '-_ []{}<>~`+=,.;:/?|';
2185
2186
	$password = '';
2187 View Code Duplication
	for ( $i = 0; $i < $length; $i++ ) {
2188
		$password .= substr($chars, wp_rand(0, strlen($chars) - 1), 1);
2189
	}
2190
2191
	/**
2192
	 * Filters the randomly-generated password.
2193
	 *
2194
	 * @since 3.0.0
2195
	 *
2196
	 * @param string $password The generated password.
2197
	 */
2198
	return apply_filters( 'random_password', $password );
2199
}
2200
endif;
2201
2202
if ( !function_exists('wp_rand') ) :
2203
/**
2204
 * Generates a random number
2205
 *
2206
 * @since 2.6.2
2207
 * @since 4.4.0 Uses PHP7 random_int() or the random_compat library if available.
2208
 *
2209
 * @global string $rnd_value
2210
 * @staticvar string $seed
2211
 * @staticvar bool $external_rand_source_available
2212
 *
2213
 * @param int $min Lower limit for the generated number
2214
 * @param int $max Upper limit for the generated number
2215
 * @return int A random number between min and max
0 ignored issues
show
Should the return type not be integer|double?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2216
 */
2217
function wp_rand( $min = 0, $max = 0 ) {
2218
	global $rnd_value;
2219
2220
	// Some misconfigured 32bit environments (Entropy PHP, for example) truncate integers larger than PHP_INT_MAX to PHP_INT_MAX rather than overflowing them to floats.
2221
	$max_random_number = 3000000000 === 2147483647 ? (float) "4294967295" : 4294967295; // 4294967295 = 0xffffffff
2222
2223
	// We only handle Ints, floats are truncated to their integer value.
2224
	$min = (int) $min;
2225
	$max = (int) $max;
2226
2227
	// Use PHP's CSPRNG, or a compatible method
2228
	static $use_random_int_functionality = true;
2229
	if ( $use_random_int_functionality ) {
2230
		try {
2231
			$_max = ( 0 != $max ) ? $max : $max_random_number;
2232
			// wp_rand() can accept arguments in either order, PHP cannot.
2233
			$_max = max( $min, $_max );
2234
			$_min = min( $min, $_max );
2235
			$val = random_int( $_min, $_max );
2236
			if ( false !== $val ) {
2237
				return absint( $val );
2238
			} else {
2239
				$use_random_int_functionality = false;
2240
			}
2241
		} catch ( Error $e ) {
2242
			$use_random_int_functionality = false;
2243
		} catch ( Exception $e ) {
2244
			$use_random_int_functionality = false;
2245
		}
2246
	}
2247
2248
	// Reset $rnd_value after 14 uses
2249
	// 32(md5) + 40(sha1) + 40(sha1) / 8 = 14 random numbers from $rnd_value
2250
	if ( strlen($rnd_value) < 8 ) {
2251
		if ( defined( 'WP_SETUP_CONFIG' ) )
2252
			static $seed = '';
2253
		else
2254
			$seed = get_transient('random_seed');
2255
		$rnd_value = md5( uniqid(microtime() . mt_rand(), true ) . $seed );
2256
		$rnd_value .= sha1($rnd_value);
2257
		$rnd_value .= sha1($rnd_value . $seed);
2258
		$seed = md5($seed . $rnd_value);
2259
		if ( ! defined( 'WP_SETUP_CONFIG' ) && ! defined( 'WP_INSTALLING' ) ) {
2260
			set_transient( 'random_seed', $seed );
2261
		}
2262
	}
2263
2264
	// Take the first 8 digits for our value
2265
	$value = substr($rnd_value, 0, 8);
2266
2267
	// Strip the first eight, leaving the remainder for the next call to wp_rand().
2268
	$rnd_value = substr($rnd_value, 8);
2269
2270
	$value = abs(hexdec($value));
2271
2272
	// Reduce the value to be within the min - max range
2273
	if ( $max != 0 )
2274
		$value = $min + ( $max - $min + 1 ) * $value / ( $max_random_number + 1 );
2275
2276
	return abs(intval($value));
2277
}
2278
endif;
2279
2280
if ( !function_exists('wp_set_password') ) :
2281
/**
2282
 * Updates the user's password with a new encrypted one.
2283
 *
2284
 * For integration with other applications, this function can be overwritten to
2285
 * instead use the other package password checking algorithm.
2286
 *
2287
 * Please note: This function should be used sparingly and is really only meant for single-time
2288
 * application. Leveraging this improperly in a plugin or theme could result in an endless loop
2289
 * of password resets if precautions are not taken to ensure it does not execute on every page load.
2290
 *
2291
 * @since 2.5.0
2292
 *
2293
 * @global wpdb $wpdb WordPress database abstraction object.
2294
 *
2295
 * @param string $password The plaintext new user password
2296
 * @param int    $user_id  User ID
2297
 */
2298
function wp_set_password( $password, $user_id ) {
2299
	global $wpdb;
2300
2301
	$hash = wp_hash_password( $password );
2302
	$wpdb->update($wpdb->users, array('user_pass' => $hash, 'user_activation_key' => ''), array('ID' => $user_id) );
2303
2304
	wp_cache_delete($user_id, 'users');
2305
}
2306
endif;
2307
2308
if ( !function_exists( 'get_avatar' ) ) :
2309
/**
2310
 * Retrieve the avatar `<img>` tag for a user, email address, MD5 hash, comment, or post.
2311
 *
2312
 * @since 2.5.0
2313
 * @since 4.2.0 Optional `$args` parameter added.
2314
 *
2315
 * @param mixed $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash,
2316
 *                           user email, WP_User object, WP_Post object, or WP_Comment object.
2317
 * @param int    $size       Optional. Height and width of the avatar image file in pixels. Default 96.
2318
 * @param string $default    Optional. URL for the default image or a default type. Accepts '404'
2319
 *                           (return a 404 instead of a default image), 'retro' (8bit), 'monsterid'
2320
 *                           (monster), 'wavatar' (cartoon face), 'indenticon' (the "quilt"),
2321
 *                           'mystery', 'mm', or 'mysteryman' (The Oyster Man), 'blank' (transparent GIF),
2322
 *                           or 'gravatar_default' (the Gravatar logo). Default is the value of the
2323
 *                           'avatar_default' option, with a fallback of 'mystery'.
2324
 * @param string $alt        Optional. Alternative text to use in &lt;img&gt; tag. Default empty.
2325
 * @param array  $args       {
0 ignored issues
show
Should the type for parameter $args not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2326
 *     Optional. Extra arguments to retrieve the avatar.
2327
 *
2328
 *     @type int          $height        Display height of the avatar in pixels. Defaults to $size.
2329
 *     @type int          $width         Display width of the avatar in pixels. Defaults to $size.
2330
 *     @type bool         $force_default Whether to always show the default image, never the Gravatar. Default false.
2331
 *     @type string       $rating        What rating to display avatars up to. Accepts 'G', 'PG', 'R', 'X', and are
2332
 *                                       judged in that order. Default is the value of the 'avatar_rating' option.
2333
 *     @type string       $scheme        URL scheme to use. See set_url_scheme() for accepted values.
2334
 *                                       Default null.
2335
 *     @type array|string $class         Array or string of additional classes to add to the &lt;img&gt; element.
2336
 *                                       Default null.
2337
 *     @type bool         $force_display Whether to always show the avatar - ignores the show_avatars option.
2338
 *                                       Default false.
2339
 *     @type string       $extra_attr    HTML attributes to insert in the IMG element. Is not sanitized. Default empty.
2340
 * }
2341
 * @return false|string `<img>` tag for the user's avatar. False on failure.
2342
 */
2343
function get_avatar( $id_or_email, $size = 96, $default = '', $alt = '', $args = null ) {
2344
	$defaults = array(
2345
		// get_avatar_data() args.
2346
		'size'          => 96,
2347
		'height'        => null,
2348
		'width'         => null,
2349
		'default'       => get_option( 'avatar_default', 'mystery' ),
2350
		'force_default' => false,
2351
		'rating'        => get_option( 'avatar_rating' ),
2352
		'scheme'        => null,
2353
		'alt'           => '',
2354
		'class'         => null,
2355
		'force_display' => false,
2356
		'extra_attr'    => '',
2357
	);
2358
2359
	if ( empty( $args ) ) {
2360
		$args = array();
2361
	}
2362
2363
	$args['size']    = (int) $size;
2364
	$args['default'] = $default;
2365
	$args['alt']     = $alt;
2366
2367
	$args = wp_parse_args( $args, $defaults );
2368
2369
	if ( empty( $args['height'] ) ) {
2370
		$args['height'] = $args['size'];
2371
	}
2372
	if ( empty( $args['width'] ) ) {
2373
		$args['width'] = $args['size'];
2374
	}
2375
2376
	if ( is_object( $id_or_email ) && isset( $id_or_email->comment_ID ) ) {
2377
		$id_or_email = get_comment( $id_or_email );
2378
	}
2379
2380
	/**
2381
	 * Filters whether to retrieve the avatar URL early.
2382
	 *
2383
	 * Passing a non-null value will effectively short-circuit get_avatar(), passing
2384
	 * the value through the {@see 'get_avatar'} filter and returning early.
2385
	 *
2386
	 * @since 4.2.0
2387
	 *
2388
	 * @param string $avatar      HTML for the user's avatar. Default null.
2389
	 * @param mixed  $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash,
2390
	 *                            user email, WP_User object, WP_Post object, or WP_Comment object.
2391
	 * @param array  $args        Arguments passed to get_avatar_url(), after processing.
2392
	 */
2393
	$avatar = apply_filters( 'pre_get_avatar', null, $id_or_email, $args );
2394
2395
	if ( ! is_null( $avatar ) ) {
2396
		/** This filter is documented in wp-includes/pluggable.php */
2397
		return apply_filters( 'get_avatar', $avatar, $id_or_email, $args['size'], $args['default'], $args['alt'], $args );
2398
	}
2399
2400
	if ( ! $args['force_display'] && ! get_option( 'show_avatars' ) ) {
2401
		return false;
2402
	}
2403
2404
	$url2x = get_avatar_url( $id_or_email, array_merge( $args, array( 'size' => $args['size'] * 2 ) ) );
2405
2406
	$args = get_avatar_data( $id_or_email, $args );
2407
2408
	$url = $args['url'];
2409
2410
	if ( ! $url || is_wp_error( $url ) ) {
2411
		return false;
2412
	}
2413
2414
	$class = array( 'avatar', 'avatar-' . (int) $args['size'], 'photo' );
2415
2416
	if ( ! $args['found_avatar'] || $args['force_default'] ) {
2417
		$class[] = 'avatar-default';
2418
	}
2419
2420
	if ( $args['class'] ) {
2421
		if ( is_array( $args['class'] ) ) {
2422
			$class = array_merge( $class, $args['class'] );
2423
		} else {
2424
			$class[] = $args['class'];
2425
		}
2426
	}
2427
2428
	$avatar = sprintf(
2429
		"<img alt='%s' src='%s' srcset='%s' class='%s' height='%d' width='%d' %s/>",
2430
		esc_attr( $args['alt'] ),
2431
		esc_url( $url ),
2432
		esc_attr( "$url2x 2x" ),
2433
		esc_attr( join( ' ', $class ) ),
2434
		(int) $args['height'],
2435
		(int) $args['width'],
2436
		$args['extra_attr']
2437
	);
2438
2439
	/**
2440
	 * Filters the avatar to retrieve.
2441
	 *
2442
	 * @since 2.5.0
2443
	 * @since 4.2.0 The `$args` parameter was added.
2444
	 *
2445
	 * @param string $avatar      &lt;img&gt; tag for the user's avatar.
2446
	 * @param mixed  $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash,
2447
	 *                            user email, WP_User object, WP_Post object, or WP_Comment object.
2448
	 * @param int    $size        Square avatar width and height in pixels to retrieve.
2449
	 * @param string $default     URL for the default image or a default type. Accepts '404', 'retro', 'monsterid',
2450
	 *                            'wavatar', 'indenticon','mystery' (or 'mm', or 'mysteryman'), 'blank', or 'gravatar_default'.
2451
	 *                            Default is the value of the 'avatar_default' option, with a fallback of 'mystery'.
2452
	 * @param string $alt         Alternative text to use in the avatar image tag. Default empty.
2453
	 * @param array  $args        Arguments passed to get_avatar_data(), after processing.
2454
	 */
2455
	return apply_filters( 'get_avatar', $avatar, $id_or_email, $args['size'], $args['default'], $args['alt'], $args );
2456
}
2457
endif;
2458
2459
if ( !function_exists( 'wp_text_diff' ) ) :
2460
/**
2461
 * Displays a human readable HTML representation of the difference between two strings.
2462
 *
2463
 * The Diff is available for getting the changes between versions. The output is
2464
 * HTML, so the primary use is for displaying the changes. If the two strings
2465
 * are equivalent, then an empty string will be returned.
2466
 *
2467
 * The arguments supported and can be changed are listed below.
2468
 *
2469
 * 'title' : Default is an empty string. Titles the diff in a manner compatible
2470
 *		with the output.
2471
 * 'title_left' : Default is an empty string. Change the HTML to the left of the
2472
 *		title.
2473
 * 'title_right' : Default is an empty string. Change the HTML to the right of
2474
 *		the title.
2475
 *
2476
 * @since 2.6.0
2477
 *
2478
 * @see wp_parse_args() Used to change defaults to user defined settings.
2479
 * @uses Text_Diff
2480
 * @uses WP_Text_Diff_Renderer_Table
2481
 *
2482
 * @param string       $left_string  "old" (left) version of string
2483
 * @param string       $right_string "new" (right) version of string
2484
 * @param string|array $args         Optional. Change 'title', 'title_left', and 'title_right' defaults.
0 ignored issues
show
Should the type for parameter $args not be string|array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2485
 * @return string Empty string if strings are equivalent or HTML with differences.
2486
 */
2487
function wp_text_diff( $left_string, $right_string, $args = null ) {
2488
	$defaults = array( 'title' => '', 'title_left' => '', 'title_right' => '' );
2489
	$args = wp_parse_args( $args, $defaults );
0 ignored issues
show
It seems like $args can also be of type null; however, wp_parse_args() does only seem to accept string|array|object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
2490
2491
	if ( ! class_exists( 'WP_Text_Diff_Renderer_Table', false ) )
2492
		require( ABSPATH . WPINC . '/wp-diff.php' );
2493
2494
	$left_string  = normalize_whitespace($left_string);
2495
	$right_string = normalize_whitespace($right_string);
2496
2497
	$left_lines  = explode("\n", $left_string);
2498
	$right_lines = explode("\n", $right_string);
2499
	$text_diff = new Text_Diff($left_lines, $right_lines);
2500
	$renderer  = new WP_Text_Diff_Renderer_Table( $args );
2501
	$diff = $renderer->render($text_diff);
2502
2503
	if ( !$diff )
2504
		return '';
2505
2506
	$r  = "<table class='diff'>\n";
2507
2508
	if ( ! empty( $args[ 'show_split_view' ] ) ) {
2509
		$r .= "<col class='content diffsplit left' /><col class='content diffsplit middle' /><col class='content diffsplit right' />";
2510
	} else {
2511
		$r .= "<col class='content' />";
2512
	}
2513
2514
	if ( $args['title'] || $args['title_left'] || $args['title_right'] )
2515
		$r .= "<thead>";
2516
	if ( $args['title'] )
2517
		$r .= "<tr class='diff-title'><th colspan='4'>$args[title]</th></tr>\n";
2518
	if ( $args['title_left'] || $args['title_right'] ) {
2519
		$r .= "<tr class='diff-sub-title'>\n";
2520
		$r .= "\t<td></td><th>$args[title_left]</th>\n";
2521
		$r .= "\t<td></td><th>$args[title_right]</th>\n";
2522
		$r .= "</tr>\n";
2523
	}
2524
	if ( $args['title'] || $args['title_left'] || $args['title_right'] )
2525
		$r .= "</thead>\n";
2526
2527
	$r .= "<tbody>\n$diff\n</tbody>\n";
2528
	$r .= "</table>";
2529
2530
	return $r;
2531
}
2532
endif;
2533
2534