run:R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:52
R W Run
DIR
2026-03-11 16:18:51
R W Run
DIR
2026-03-11 16:18:51
R W Run
23.8 KB
2026-03-11 16:18:51
R W Run
7.8 KB
2026-03-11 16:18:52
R W Run
36.1 KB
2026-03-11 16:18:51
R W Run
11.9 KB
2026-03-11 16:18:52
R W Run
18.94 KB
2026-03-11 16:18:52
R W Run
7.35 KB
2026-03-11 16:18:52
R W Run
28.6 KB
2026-03-11 16:18:51
R W Run
316 By
2026-03-11 16:18:51
R W Run
12.9 KB
2026-03-11 16:18:51
R W Run
61.02 KB
2026-03-11 16:18:52
R W Run
15 KB
2026-03-11 16:18:51
R W Run
112.05 KB
2026-03-11 16:18:51
R W Run
12.47 KB
2026-03-11 16:18:51
R W Run
15.07 KB
2026-03-11 16:18:52
R W Run
9.84 KB
2026-03-11 16:18:52
R W Run
13.17 KB
2026-03-11 16:18:52
R W Run
33.83 KB
2026-03-11 16:18:51
R W Run
42.63 KB
2026-03-11 16:18:51
R W Run
55.71 KB
2026-03-11 16:18:52
R W Run
12.53 KB
2026-03-11 16:18:51
R W Run
2.55 KB
2026-03-11 16:18:52
R W Run
28.92 KB
2026-03-11 16:18:52
R W Run
539 By
2026-03-11 16:18:51
R W Run
367 By
2026-03-11 16:18:52
R W Run
42.65 KB
2026-03-11 16:18:51
R W Run
401 By
2026-03-11 16:18:51
R W Run
6.61 KB
2026-03-11 16:18:51
R W Run
664 By
2026-03-11 16:18:52
R W Run
20.63 KB
2026-03-11 16:18:51
R W Run
2.18 KB
2026-03-11 16:18:52
R W Run
453 By
2026-03-11 16:18:52
R W Run
457 By
2026-03-11 16:18:51
R W Run
36.83 KB
2026-03-11 16:18:52
R W Run
2.41 KB
2026-03-11 16:18:52
R W Run
8.28 KB
2026-03-11 16:18:51
R W Run
13.89 KB
2026-03-11 16:18:51
R W Run
11.76 KB
2026-03-11 16:18:51
R W Run
2.65 KB
2026-03-11 16:18:51
R W Run
7.43 KB
2026-03-11 16:18:51
R W Run
17.46 KB
2026-03-11 16:18:51
R W Run
5.14 KB
2026-03-11 16:18:52
R W Run
16.7 KB
2026-03-11 16:18:51
R W Run
8.28 KB
2026-03-11 16:18:52
R W Run
2.92 KB
2026-03-11 16:18:52
R W Run
1.32 KB
2026-03-11 16:18:51
R W Run
4.6 KB
2026-03-11 16:18:52
R W Run
11.62 KB
2026-03-11 16:18:52
R W Run
2.5 KB
2026-03-11 16:18:51
R W Run
1.97 KB
2026-03-11 16:18:51
R W Run
11.25 KB
2026-03-11 16:18:52
R W Run
5.32 KB
2026-03-11 16:18:51
R W Run
10.99 KB
2026-03-11 16:18:52
R W Run
68.32 KB
2026-03-11 16:18:51
R W Run
6.34 KB
2026-03-11 16:18:51
R W Run
5.49 KB
2026-03-11 16:18:51
R W Run
1.99 KB
2026-03-11 16:18:52
R W Run
7.02 KB
2026-03-11 16:18:51
R W Run
4.91 KB
2026-03-11 16:18:52
R W Run
16.86 KB
2026-03-11 16:18:51
R W Run
24.23 KB
2026-03-11 16:18:51
R W Run
3.97 KB
2026-03-11 16:18:51
R W Run
47.66 KB
2026-03-11 16:18:51
R W Run
9.22 KB
2026-03-11 16:18:51
R W Run
25.51 KB
2026-03-11 16:18:51
R W Run
198.38 KB
2026-03-11 16:18:52
R W Run
56.65 KB
2026-03-11 16:18:51
R W Run
10.46 KB
2026-03-11 16:18:51
R W Run
10.95 KB
2026-03-11 16:18:52
R W Run
29.26 KB
2026-03-11 16:18:51
R W Run
70.91 KB
2026-03-11 16:18:52
R W Run
35.3 KB
2026-03-11 16:18:52
R W Run
16.61 KB
2026-03-11 16:18:52
R W Run
2.57 KB
2026-03-11 16:18:52
R W Run
39.83 KB
2026-03-11 16:18:51
R W Run
70.64 KB
2026-03-11 16:18:51
R W Run
15.56 KB
2026-03-11 16:18:52
R W Run
7.33 KB
2026-03-11 16:18:52
R W Run
253 By
2026-03-11 16:18:51
R W Run
7.96 KB
2026-03-11 16:18:52
R W Run
3.23 KB
2026-03-11 16:18:52
R W Run
969 By
2026-03-11 16:18:52
R W Run
16.28 KB
2026-03-11 16:18:51
R W Run
7.22 KB
2026-03-11 16:18:51
R W Run
12.95 KB
2026-03-11 16:18:51
R W Run
6.53 KB
2026-03-11 16:18:51
R W Run
3.42 KB
2026-03-11 16:18:52
R W Run
5.84 KB
2026-03-11 16:18:51
R W Run
1.97 KB
2026-03-11 16:18:51
R W Run
4.3 KB
2026-03-11 16:18:52
R W Run
2.91 KB
2026-03-11 16:18:51
R W Run
16.46 KB
2026-03-11 16:18:52
R W Run
40.6 KB
2026-03-11 16:18:51
R W Run
20.22 KB
2026-03-11 16:18:51
R W Run
36.11 KB
2026-03-11 16:18:52
R W Run
17.01 KB
2026-03-11 16:18:51
R W Run
7.27 KB
2026-03-11 16:18:52
R W Run
6.62 KB
2026-03-11 16:18:52
R W Run
16.49 KB
2026-03-11 16:18:52
R W Run
1.79 KB
2026-03-11 16:18:52
R W Run
29.82 KB
2026-03-11 16:18:51
R W Run
6.67 KB
2026-03-11 16:18:52
R W Run
8.98 KB
2026-03-11 16:18:52
R W Run
19.42 KB
2026-03-11 16:18:51
R W Run
12.01 KB
2026-03-11 16:18:51
R W Run
17.11 KB
2026-03-11 16:18:51
R W Run
6.74 KB
2026-03-11 16:18:52
R W Run
30.93 KB
2026-03-11 16:18:51
R W Run
4.99 KB
2026-03-11 16:18:51
R W Run
4.25 KB
2026-03-11 16:18:51
R W Run
24.72 KB
2026-03-11 16:18:51
R W Run
29.96 KB
2026-03-11 16:18:52
R W Run
6.41 KB
2026-03-11 16:18:51
R W Run
160 KB
2026-03-11 16:18:51
R W Run
6.72 KB
2026-03-11 16:18:52
R W Run
10.92 KB
2026-03-11 16:18:51
R W Run
4.77 KB
2026-03-11 16:18:51
R W Run
3.38 KB
2026-03-11 16:18:51
R W Run
11.18 KB
2026-03-11 16:18:51
R W Run
62.19 KB
2026-03-11 16:18:51
R W Run
2.46 KB
2026-03-11 16:18:51
R W Run
9.17 KB
2026-03-11 16:18:51
R W Run
32.15 KB
2026-03-11 16:18:51
R W Run
34.05 KB
2026-03-11 16:18:52
R W Run
7.15 KB
2026-03-11 16:18:51
R W Run
3.47 KB
2026-03-11 16:18:52
R W Run
1.87 KB
2026-03-11 16:18:52
R W Run
30.91 KB
2026-03-11 16:18:51
R W Run
7.29 KB
2026-03-11 16:18:52
R W Run
7.35 KB
2026-03-11 16:18:51
R W Run
12.54 KB
2026-03-11 16:18:51
R W Run
19.12 KB
2026-03-11 16:18:51
R W Run
18.12 KB
2026-03-11 16:18:52
R W Run
39.99 KB
2026-03-11 16:18:52
R W Run
5.17 KB
2026-03-11 16:18:52
R W Run
979 By
2026-03-11 16:18:51
R W Run
18.44 KB
2026-03-11 16:18:52
R W Run
10.24 KB
2026-03-11 16:18:51
R W Run
1.77 KB
2026-03-11 16:18:52
R W Run
34.9 KB
2026-03-11 16:18:51
R W Run
7.19 KB
2026-03-11 16:18:52
R W Run
160.5 KB
2026-03-11 16:18:51
R W Run
64.27 KB
2026-03-11 16:18:51
R W Run
27.95 KB
2026-03-11 16:18:51
R W Run
4.69 KB
2026-03-11 16:18:51
R W Run
2.94 KB
2026-03-11 16:18:51
R W Run
43.13 KB
2026-03-11 16:18:52
R W Run
2.25 KB
2026-03-11 16:18:52
R W Run
22.5 KB
2026-03-11 16:18:51
R W Run
13.01 KB
2026-03-11 16:18:52
R W Run
3.27 KB
2026-03-11 16:18:51
R W Run
18 KB
2026-03-11 16:18:51
R W Run
210.4 KB
2026-03-11 16:18:52
R W Run
25.86 KB
2026-03-11 16:18:52
R W Run
115.85 KB
2026-03-11 16:18:51
R W Run
373 By
2026-03-11 16:18:52
R W Run
343 By
2026-03-11 16:18:52
R W Run
338 By
2026-03-11 16:18:51
R W Run
100.73 KB
2026-03-11 16:18:52
R W Run
130.93 KB
2026-03-11 16:18:51
R W Run
19.1 KB
2026-03-11 16:18:51
R W Run
17.41 KB
2026-03-11 16:18:52
R W Run
41.98 KB
2026-03-11 16:18:52
R W Run
400 By
2026-03-11 16:18:52
R W Run
11.1 KB
2026-03-11 16:18:52
R W Run
37.02 KB
2026-03-11 16:18:51
R W Run
2.24 KB
2026-03-11 16:18:51
R W Run
188.13 KB
2026-03-11 16:18:51
R W Run
338 By
2026-03-11 16:18:51
R W Run
38 KB
2026-03-11 16:18:51
R W Run
4.02 KB
2026-03-11 16:18:52
R W Run
5.38 KB
2026-03-11 16:18:51
R W Run
3.05 KB
2026-03-11 16:18:52
R W Run
2.61 KB
2026-03-11 16:18:51
R W Run
1.16 KB
2026-03-11 16:18:52
R W Run
4.04 KB
2026-03-11 16:18:51
R W Run
3.71 KB
2026-03-11 16:18:51
R W Run
24.6 KB
2026-03-11 16:18:51
R W Run
9.56 KB
2026-03-11 16:18:51
R W Run
346.43 KB
2026-03-11 16:18:52
R W Run
281.84 KB
2026-03-11 16:18:52
R W Run
14.95 KB
2026-03-11 16:18:51
R W Run
8.44 KB
2026-03-11 16:18:52
R W Run
168.95 KB
2026-03-11 16:18:52
R W Run
20.71 KB
2026-03-11 16:18:52
R W Run
25.27 KB
2026-03-11 16:18:51
R W Run
5.72 KB
2026-03-11 16:18:51
R W Run
4.63 KB
2026-03-11 16:18:52
R W Run
81.73 KB
2026-03-11 16:18:51
R W Run
67.18 KB
2026-03-11 16:18:51
R W Run
156.36 KB
2026-03-11 16:18:52
R W Run
55.19 KB
2026-03-11 16:18:51
R W Run
162 By
2026-03-11 16:18:51
R W Run
61.72 KB
2026-03-11 16:18:51
R W Run
216.06 KB
2026-03-11 16:18:52
R W Run
65.09 KB
2026-03-11 16:18:51
R W Run
25.24 KB
2026-03-11 16:18:52
R W Run
4.81 KB
2026-03-11 16:18:51
R W Run
6.48 KB
2026-03-11 16:18:52
R W Run
21.25 KB
2026-03-11 16:18:51
R W Run
2.79 KB
2026-03-11 16:18:52
R W Run
89.69 KB
2026-03-11 16:18:52
R W Run
19.42 KB
2026-03-11 16:18:52
R W Run
3.69 KB
2026-03-11 16:18:52
R W Run
4.11 KB
2026-03-11 16:18:51
R W Run
40.74 KB
2026-03-11 16:18:51
R W Run
25.38 KB
2026-03-11 16:18:51
R W Run
43.31 KB
2026-03-11 16:18:52
R W Run
102.57 KB
2026-03-11 16:18:52
R W Run
6.18 KB
2026-03-11 16:18:51
R W Run
124.47 KB
2026-03-11 16:18:52
R W Run
35.65 KB
2026-03-11 16:18:52
R W Run
6.94 KB
2026-03-11 16:18:52
R W Run
67.04 KB
2026-03-11 16:18:52
R W Run
10.62 KB
2026-03-11 16:18:51
R W Run
289.35 KB
2026-03-11 16:18:52
R W Run
36.23 KB
2026-03-11 16:18:51
R W Run
200 By
2026-03-11 16:18:52
R W Run
200 By
2026-03-11 16:18:52
R W Run
98.29 KB
2026-03-11 16:18:52
R W Run
30.02 KB
2026-03-11 16:18:52
R W Run
19.03 KB
2026-03-11 16:18:52
R W Run
5.06 KB
2026-03-11 16:18:52
R W Run
255 By
2026-03-11 16:18:51
R W Run
22.66 KB
2026-03-11 16:18:52
R W Run
154.63 KB
2026-03-11 16:18:51
R W Run
9.68 KB
2026-03-11 16:18:51
R W Run
258 By
2026-03-11 16:18:51
R W Run
23.49 KB
2026-03-11 16:18:51
R W Run
3.16 KB
2026-03-11 16:18:51
R W Run
8.4 KB
2026-03-11 16:18:52
R W Run
441 By
2026-03-11 16:18:51
R W Run
7.39 KB
2026-03-11 16:18:51
R W Run
173 KB
2026-03-11 16:18:52
R W Run
544 By
2026-03-11 16:18:52
R W Run
4.17 KB
2026-03-11 16:18:51
R W Run
35.97 KB
2026-03-11 16:18:52
R W Run
1.69 KB
2026-03-11 16:18:51
R W Run
2.84 KB
2026-03-11 16:18:52
R W Run
6.09 KB
2026-03-11 16:18:51
R W Run
8.71 KB
2026-03-11 16:18:51
R W Run
131.84 KB
2026-03-11 16:18:51
R W Run
37.45 KB
2026-03-11 16:18:51
R W Run
173.89 KB
2026-03-11 16:18:51
R W Run
7.09 KB
2026-03-11 16:18:51
R W Run
6.41 KB
2026-03-11 16:18:51
R W Run
1.08 KB
2026-03-11 16:18:51
R W Run
69.46 KB
2026-03-11 16:18:52
R W Run
445 By
2026-03-11 16:18:51
R W Run
799 By
2026-03-11 16:18:52
R W Run
error_log
📄pluggable.php
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
9if ( ! 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 *
21 * @global WP_User $current_user The current user object which holds the user data.
22 *
23 * @param int|null $id User ID.
24 * @param string $name User's username.
25 * @return WP_User Current user User object.
26 */
27 function wp_set_current_user( $id, $name = '' ) {
28 global $current_user;
29
30 // If `$id` matches the current user, there is nothing to do.
31 if ( isset( $current_user )
32 && ( $current_user instanceof WP_User )
33 && ( $id === $current_user->ID )
34 && ( null !== $id )
35 ) {
36 return $current_user;
37 }
38
39 $current_user = new WP_User( $id, $name );
40
41 setup_userdata( $current_user->ID );
42
43 /**
44 * Fires after the current user is set.
45 *
46 * @since 2.0.1
47 */
48 do_action( 'set_current_user' );
49
50 return $current_user;
51 }
52endif;
53
54if ( ! function_exists( 'wp_get_current_user' ) ) :
55 /**
56 * Retrieves the current user object.
57 *
58 * Will set the current user, if the current user is not set. The current user
59 * will be set to the logged-in person. If no user is logged-in, then it will
60 * set the current user to 0, which is invalid and won't have any permissions.
61 *
62 * @since 2.0.3
63 *
64 * @see _wp_get_current_user()
65 * @global WP_User $current_user Checks if the current user is set.
66 *
67 * @return WP_User Current WP_User instance.
68 */
69 function wp_get_current_user() {
70 return _wp_get_current_user();
71 }
72endif;
73
74if ( ! function_exists( 'get_userdata' ) ) :
75 /**
76 * Retrieves user info by user ID.
77 *
78 * @since 0.71
79 *
80 * @param int $user_id User ID
81 * @return WP_User|false WP_User object on success, false on failure.
82 */
83 function get_userdata( $user_id ) {
84 return get_user_by( 'id', $user_id );
85 }
86endif;
87
88if ( ! function_exists( 'get_user_by' ) ) :
89 /**
90 * Retrieves user info by a given field.
91 *
92 * @since 2.8.0
93 * @since 4.4.0 Added 'ID' as an alias of 'id' for the `$field` parameter.
94 *
95 * @global WP_User $current_user The current user object which holds the user data.
96 *
97 * @param string $field The field to retrieve the user with. id | ID | slug | email | login.
98 * @param int|string $value A value for $field. A user ID, slug, email address, or login name.
99 * @return WP_User|false WP_User object on success, false on failure.
100 */
101 function get_user_by( $field, $value ) {
102 $userdata = WP_User::get_data_by( $field, $value );
103
104 if ( ! $userdata ) {
105 return false;
106 }
107
108 $user = new WP_User();
109 $user->init( $userdata );
110
111 return $user;
112 }
113endif;
114
115if ( ! function_exists( 'cache_users' ) ) :
116 /**
117 * Retrieves info for user lists to prevent multiple queries by get_userdata().
118 *
119 * @since 3.0.0
120 *
121 * @global wpdb $wpdb WordPress database abstraction object.
122 *
123 * @param int[] $user_ids User ID numbers list
124 */
125 function cache_users( $user_ids ) {
126 global $wpdb;
127
128 update_meta_cache( 'user', $user_ids );
129
130 $clean = _get_non_cached_ids( $user_ids, 'users' );
131
132 if ( empty( $clean ) ) {
133 return;
134 }
135
136 $list = implode( ',', $clean );
137
138 $users = $wpdb->get_results( "SELECT * FROM $wpdb->users WHERE ID IN ($list)" );
139
140 foreach ( $users as $user ) {
141 update_user_caches( $user );
142 }
143 }
144endif;
145
146if ( ! function_exists( 'wp_mail' ) ) :
147 /**
148 * Sends an email, similar to PHP's mail function.
149 *
150 * A true return value does not automatically mean that the user received the
151 * email successfully. It just only means that the method used was able to
152 * process the request without any errors.
153 *
154 * The default content type is `text/plain` which does not allow using HTML.
155 * However, you can set the content type of the email by using the
156 * {@see 'wp_mail_content_type'} filter.
157 *
158 * The default charset is based on the charset used on the blog. The charset can
159 * be set using the {@see 'wp_mail_charset'} filter.
160 *
161 * When using the `$embeds` parameter to embed images for use in HTML emails,
162 * reference the embedded file in your HTML with a `cid:` URL whose value
163 * matches the file's Content-ID. By default, the Content-ID (`cid`) used for
164 * each embedded file is the key in the embeds array, unless modified via the
165 * {@see 'wp_mail_embed_args'} filter. For example:
166 *
167 * `<img src="cid:0" alt="Logo">`
168 * `<img src="cid:my-image" alt="Image">`
169 *
170 * You may also customize the Content-ID for each file by using the
171 * {@see 'wp_mail_embed_args'} filter and setting the `cid` value.
172 *
173 * @since 1.2.1
174 * @since 5.5.0 is_email() is used for email validation,
175 * instead of PHPMailer's default validator.
176 * @since 6.9.0 Added $embeds parameter.
177 * @since 6.9.0 Improved Content-Type header handling for multipart messages.
178 *
179 * @global PHPMailer\PHPMailer\PHPMailer $phpmailer
180 *
181 * @param string|string[] $to Array or comma-separated list of email addresses to send message.
182 * @param string $subject Email subject.
183 * @param string $message Message contents.
184 * @param string|string[] $headers Optional. Additional headers.
185 * @param string|string[] $attachments Optional. Paths to files to attach.
186 * @param string|string[] $embeds Optional. Paths to files to embed.
187 * @return bool Whether the email was sent successfully.
188 */
189 function wp_mail( $to, $subject, $message, $headers = '', $attachments = array(), $embeds = array() ) {
190 // Compact the input, apply the filters, and extract them back out.
191
192 /**
193 * Filters the wp_mail() arguments.
194 *
195 * @since 2.2.0
196 *
197 * @param array $args {
198 * Array of the `wp_mail()` arguments.
199 *
200 * @type string|string[] $to Array or comma-separated list of email addresses to send message.
201 * @type string $subject Email subject.
202 * @type string $message Message contents.
203 * @type string|string[] $headers Additional headers.
204 * @type string|string[] $attachments Paths to files to attach.
205 * @type string|string[] $embeds Paths to files to embed.
206 * }
207 */
208 $atts = apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments', 'embeds' ) );
209
210 /**
211 * Filters whether to preempt sending an email.
212 *
213 * Returning a non-null value will short-circuit {@see wp_mail()}, returning
214 * that value instead. A boolean return value should be used to indicate whether
215 * the email was successfully sent.
216 *
217 * @since 5.7.0
218 *
219 * @param null|bool $return Short-circuit return value.
220 * @param array $atts {
221 * Array of the `wp_mail()` arguments.
222 *
223 * @type string|string[] $to Array or comma-separated list of email addresses to send message.
224 * @type string $subject Email subject.
225 * @type string $message Message contents.
226 * @type string|string[] $headers Additional headers.
227 * @type string|string[] $attachments Paths to files to attach.
228 * @type string|string[] $embeds Paths to files to embed.
229 * }
230 */
231 $pre_wp_mail = apply_filters( 'pre_wp_mail', null, $atts );
232
233 if ( null !== $pre_wp_mail ) {
234 return $pre_wp_mail;
235 }
236
237 if ( isset( $atts['to'] ) ) {
238 $to = $atts['to'];
239 }
240
241 if ( ! is_array( $to ) ) {
242 $to = explode( ',', $to );
243 }
244
245 if ( isset( $atts['subject'] ) ) {
246 $subject = $atts['subject'];
247 }
248
249 if ( isset( $atts['message'] ) ) {
250 $message = $atts['message'];
251 }
252
253 if ( isset( $atts['headers'] ) ) {
254 $headers = $atts['headers'];
255 }
256
257 if ( isset( $atts['attachments'] ) ) {
258 $attachments = $atts['attachments'];
259 }
260
261 if ( ! is_array( $attachments ) ) {
262 $attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) );
263 }
264
265 if ( isset( $atts['embeds'] ) ) {
266 $embeds = $atts['embeds'];
267 }
268
269 if ( ! is_array( $embeds ) ) {
270 $embeds = explode( "\n", str_replace( "\r\n", "\n", $embeds ) );
271 }
272
273 global $phpmailer;
274
275 // (Re)create it, if it's gone missing.
276 if ( ! ( $phpmailer instanceof PHPMailer\PHPMailer\PHPMailer ) ) {
277 require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
278 require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php';
279 require_once ABSPATH . WPINC . '/PHPMailer/Exception.php';
280 require_once ABSPATH . WPINC . '/class-wp-phpmailer.php';
281 $phpmailer = new WP_PHPMailer( true );
282
283 $phpmailer::$validator = static function ( $email ) {
284 return (bool) is_email( $email );
285 };
286 }
287
288 // Headers.
289 $cc = array();
290 $bcc = array();
291 $reply_to = array();
292
293 if ( empty( $headers ) ) {
294 $headers = array();
295 } else {
296 if ( ! is_array( $headers ) ) {
297 /*
298 * Explode the headers out, so this function can take
299 * both string headers and an array of headers.
300 */
301 $tempheaders = explode( "\n", str_replace( "\r\n", "\n", $headers ) );
302 } else {
303 $tempheaders = $headers;
304 }
305 $headers = array();
306
307 // If it's actually got contents.
308 if ( ! empty( $tempheaders ) ) {
309 // Iterate through the raw headers.
310 foreach ( (array) $tempheaders as $header ) {
311 if ( ! str_contains( $header, ':' ) ) {
312 if ( false !== stripos( $header, 'boundary=' ) ) {
313 $parts = preg_split( '/boundary=/i', trim( $header ) );
314 $boundary = trim( str_replace( array( "'", '"' ), '', $parts[1] ) );
315 }
316 continue;
317 }
318 // Explode them out.
319 list( $name, $content ) = explode( ':', trim( $header ), 2 );
320
321 // Cleanup crew.
322 $name = trim( $name );
323 $content = trim( $content );
324
325 switch ( strtolower( $name ) ) {
326 // Mainly for legacy -- process a "From:" header if it's there.
327 case 'from':
328 $bracket_pos = strpos( $content, '<' );
329 if ( false !== $bracket_pos ) {
330 // Text before the bracketed email is the "From" name.
331 if ( $bracket_pos > 0 ) {
332 $from_name = substr( $content, 0, $bracket_pos );
333 $from_name = str_replace( '"', '', $from_name );
334 $from_name = trim( $from_name );
335 }
336
337 $from_email = substr( $content, $bracket_pos + 1 );
338 $from_email = str_replace( '>', '', $from_email );
339 $from_email = trim( $from_email );
340
341 // Avoid setting an empty $from_email.
342 } elseif ( '' !== trim( $content ) ) {
343 $from_email = trim( $content );
344 }
345 break;
346 case 'content-type':
347 if ( str_contains( $content, ';' ) ) {
348 list( $type, $charset_content ) = explode( ';', $content );
349 $content_type = trim( $type );
350 if ( false !== stripos( $charset_content, 'charset=' ) ) {
351 $charset = trim( str_replace( array( 'charset=', '"' ), '', $charset_content ) );
352 } elseif ( false !== stripos( $charset_content, 'boundary=' ) ) {
353 $boundary = trim( str_replace( array( 'BOUNDARY=', 'boundary=', '"' ), '', $charset_content ) );
354 $charset = '';
355 if ( preg_match( '~^multipart/(\S+)~', $content_type, $matches ) ) {
356 $content_type = 'multipart/' . strtolower( $matches[1] ) . '; boundary="' . $boundary . '"';
357 }
358 }
359
360 // Avoid setting an empty $content_type.
361 } elseif ( '' !== trim( $content ) ) {
362 $content_type = trim( $content );
363 }
364 break;
365 case 'cc':
366 $cc = array_merge( (array) $cc, explode( ',', $content ) );
367 break;
368 case 'bcc':
369 $bcc = array_merge( (array) $bcc, explode( ',', $content ) );
370 break;
371 case 'reply-to':
372 $reply_to = array_merge( (array) $reply_to, explode( ',', $content ) );
373 break;
374 default:
375 // Add it to our grand headers array.
376 $headers[ trim( $name ) ] = trim( $content );
377 break;
378 }
379 }
380 }
381 }
382
383 // Empty out the values that may be set.
384 $phpmailer->clearAllRecipients();
385 $phpmailer->clearAttachments();
386 $phpmailer->clearCustomHeaders();
387 $phpmailer->clearReplyTos();
388 $phpmailer->Body = '';
389 $phpmailer->AltBody = '';
390
391 /*
392 * Reset encoding to 8-bit, as it may have been automatically downgraded
393 * to 7-bit by PHPMailer (based on the body contents) in a previous call
394 * to wp_mail().
395 *
396 * See https://core.trac.wordpress.org/ticket/33972
397 */
398 $phpmailer->Encoding = PHPMailer\PHPMailer\PHPMailer::ENCODING_8BIT;
399
400 // Set "From" name and email.
401
402 // If we don't have a name from the input headers.
403 if ( ! isset( $from_name ) ) {
404 $from_name = 'WordPress';
405 }
406
407 /*
408 * If we don't have an email from the input headers, default to wordpress@$sitename
409 * Some hosts will block outgoing mail from this address if it doesn't exist,
410 * but there's no easy alternative. Defaulting to admin_email might appear to be
411 * another option, but some hosts may refuse to relay mail from an unknown domain.
412 * See https://core.trac.wordpress.org/ticket/5007.
413 */
414 if ( ! isset( $from_email ) ) {
415 // Get the site domain and get rid of www.
416 $sitename = wp_parse_url( network_home_url(), PHP_URL_HOST );
417 $from_email = 'wordpress@';
418
419 if ( null !== $sitename ) {
420 if ( str_starts_with( $sitename, 'www.' ) ) {
421 $sitename = substr( $sitename, 4 );
422 }
423
424 $from_email .= $sitename;
425 }
426 }
427
428 /**
429 * Filters the email address to send from.
430 *
431 * @since 2.2.0
432 *
433 * @param string $from_email Email address to send from.
434 */
435 $from_email = apply_filters( 'wp_mail_from', $from_email );
436
437 /**
438 * Filters the name to associate with the "from" email address.
439 *
440 * @since 2.3.0
441 *
442 * @param string $from_name Name associated with the "from" email address.
443 */
444 $from_name = apply_filters( 'wp_mail_from_name', $from_name );
445
446 try {
447 $phpmailer->setFrom( $from_email, $from_name, false );
448 } catch ( PHPMailer\PHPMailer\Exception $e ) {
449 $mail_error_data = compact( 'to', 'subject', 'message', 'headers', 'attachments' );
450 $mail_error_data['phpmailer_exception_code'] = $e->getCode();
451
452 /** This filter is documented in wp-includes/pluggable.php */
453 do_action( 'wp_mail_failed', new WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_error_data ) );
454
455 return false;
456 }
457
458 // Set mail's subject and body.
459 $phpmailer->Subject = $subject;
460 $phpmailer->Body = $message;
461
462 // Set destination addresses, using appropriate methods for handling addresses.
463 $address_headers = compact( 'to', 'cc', 'bcc', 'reply_to' );
464
465 foreach ( $address_headers as $address_header => $addresses ) {
466 if ( empty( $addresses ) ) {
467 continue;
468 }
469
470 foreach ( (array) $addresses as $address ) {
471 try {
472 // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>".
473 $recipient_name = '';
474
475 if ( preg_match( '/(.*)<(.+)>/', $address, $matches ) ) {
476 if ( count( $matches ) === 3 ) {
477 $recipient_name = $matches[1];
478 $address = $matches[2];
479 }
480 }
481
482 switch ( $address_header ) {
483 case 'to':
484 $phpmailer->addAddress( $address, $recipient_name );
485 break;
486 case 'cc':
487 $phpmailer->addCC( $address, $recipient_name );
488 break;
489 case 'bcc':
490 $phpmailer->addBCC( $address, $recipient_name );
491 break;
492 case 'reply_to':
493 $phpmailer->addReplyTo( $address, $recipient_name );
494 break;
495 }
496 } catch ( PHPMailer\PHPMailer\Exception $e ) {
497 continue;
498 }
499 }
500 }
501
502 // Set to use PHP's mail().
503 $phpmailer->isMail();
504
505 // Set Content-Type and charset.
506
507 // If we don't have a Content-Type from the input headers.
508 if ( ! isset( $content_type ) ) {
509 $content_type = 'text/plain';
510 }
511
512 /**
513 * Filters the wp_mail() content type.
514 *
515 * @since 2.3.0
516 *
517 * @param string $content_type Default wp_mail() content type.
518 */
519 $content_type = apply_filters( 'wp_mail_content_type', $content_type );
520
521 $phpmailer->ContentType = $content_type;
522
523 // Set whether it's plaintext, depending on $content_type.
524 if ( 'text/html' === $content_type ) {
525 $phpmailer->isHTML( true );
526 }
527
528 // If we don't have a charset from the input headers.
529 if ( ! isset( $charset ) ) {
530 $charset = get_bloginfo( 'charset' );
531 }
532
533 /**
534 * Filters the default wp_mail() charset.
535 *
536 * @since 2.3.0
537 *
538 * @param string $charset Default email charset.
539 */
540 $phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset );
541
542 // Set custom headers.
543 if ( ! empty( $headers ) ) {
544 foreach ( (array) $headers as $name => $content ) {
545 // Only add custom headers not added automatically by PHPMailer.
546 if ( ! in_array( $name, array( 'MIME-Version', 'X-Mailer' ), true ) ) {
547 try {
548 $phpmailer->addCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) );
549 } catch ( PHPMailer\PHPMailer\Exception $e ) {
550 continue;
551 }
552 }
553 }
554 }
555
556 if ( ! empty( $attachments ) ) {
557 foreach ( $attachments as $filename => $attachment ) {
558 $filename = is_string( $filename ) ? $filename : '';
559
560 try {
561 $phpmailer->addAttachment( $attachment, $filename );
562 } catch ( PHPMailer\PHPMailer\Exception $e ) {
563 continue;
564 }
565 }
566 }
567
568 if ( ! empty( $embeds ) ) {
569 foreach ( $embeds as $key => $embed_path ) {
570 /**
571 * Filters the arguments for PHPMailer's addEmbeddedImage() method.
572 *
573 * @since 6.9.0
574 *
575 * @param array $args {
576 * An array of arguments for `addEmbeddedImage()`.
577 * @type string $path The path to the file.
578 * @type string $cid The Content-ID of the image. Default: The key in the embeds array.
579 * @type string $name The filename of the image.
580 * @type string $encoding The encoding of the image. Default: 'base64'.
581 * @type string $type The MIME type of the image. Default: empty string, which lets PHPMailer auto-detect.
582 * @type string $disposition The disposition of the image. Default: 'inline'.
583 * }
584 */
585 $embed_args = apply_filters(
586 'wp_mail_embed_args',
587 array(
588 'path' => $embed_path,
589 'cid' => (string) $key,
590 'name' => basename( $embed_path ),
591 'encoding' => 'base64',
592 'type' => '',
593 'disposition' => 'inline',
594 )
595 );
596
597 try {
598 $phpmailer->addEmbeddedImage(
599 $embed_args['path'],
600 $embed_args['cid'],
601 $embed_args['name'],
602 $embed_args['encoding'],
603 $embed_args['type'],
604 $embed_args['disposition']
605 );
606 } catch ( PHPMailer\PHPMailer\Exception $e ) {
607 continue;
608 }
609 }
610 }
611
612 /**
613 * Fires after PHPMailer is initialized.
614 *
615 * @since 2.2.0
616 *
617 * @param PHPMailer $phpmailer The PHPMailer instance (passed by reference).
618 */
619 do_action_ref_array( 'phpmailer_init', array( &$phpmailer ) );
620
621 $mail_data = compact( 'to', 'subject', 'message', 'headers', 'attachments', 'embeds' );
622
623 // Send!
624 try {
625 $send = $phpmailer->send();
626
627 /**
628 * Fires after PHPMailer has successfully sent an email.
629 *
630 * The firing of this action does not necessarily mean that the recipient(s) received the
631 * email successfully. It only means that the `send` method above was able to
632 * process the request without any errors.
633 *
634 * @since 5.9.0
635 *
636 * @param array $mail_data {
637 * An array containing the email recipient(s), subject, message, headers, and attachments.
638 *
639 * @type string[] $to Email addresses to send message.
640 * @type string $subject Email subject.
641 * @type string $message Message contents.
642 * @type string[] $headers Additional headers.
643 * @type string[] $attachments Paths to files to attach.
644 * @type string[] $embeds Paths to files to embed.
645 * }
646 */
647 do_action( 'wp_mail_succeeded', $mail_data );
648
649 return $send;
650 } catch ( PHPMailer\PHPMailer\Exception $e ) {
651 $mail_data['phpmailer_exception_code'] = $e->getCode();
652
653 /**
654 * Fires after a PHPMailer\PHPMailer\Exception is caught.
655 *
656 * @since 4.4.0
657 *
658 * @param WP_Error $error A WP_Error object with the PHPMailer\PHPMailer\Exception message, and an array
659 * containing the mail recipient, subject, message, headers, and attachments.
660 */
661 do_action( 'wp_mail_failed', new WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_data ) );
662
663 return false;
664 }
665 }
666endif;
667
668if ( ! function_exists( 'wp_authenticate' ) ) :
669 /**
670 * Authenticates a user, confirming the login credentials are valid.
671 *
672 * @since 2.5.0
673 * @since 4.5.0 `$username` now accepts an email address.
674 *
675 * @param string $username User's username or email address.
676 * @param string $password User's password.
677 * @return WP_User|WP_Error WP_User object if the credentials are valid,
678 * otherwise WP_Error.
679 */
680 function wp_authenticate(
681 $username,
682 #[\SensitiveParameter]
683 $password
684 ) {
685 $username = sanitize_user( $username );
686 $password = trim( $password );
687
688 /**
689 * Filters whether a set of user login credentials are valid.
690 *
691 * A WP_User object is returned if the credentials authenticate a user.
692 * WP_Error or null otherwise.
693 *
694 * @since 2.8.0
695 * @since 4.5.0 `$username` now accepts an email address.
696 *
697 * @param null|WP_User|WP_Error $user WP_User if the user is authenticated.
698 * WP_Error or null otherwise.
699 * @param string $username Username or email address.
700 * @param string $password User password.
701 */
702 $user = apply_filters( 'authenticate', null, $username, $password );
703
704 if ( null === $user || false === $user ) {
705 /*
706 * TODO: What should the error message be? (Or would these even happen?)
707 * Only needed if all authentication handlers fail to return anything.
708 */
709 $user = new WP_Error( 'authentication_failed', __( '<strong>Error:</strong> Invalid username, email address or incorrect password.' ) );
710 }
711
712 $ignore_codes = array( 'empty_username', 'empty_password' );
713
714 if ( is_wp_error( $user ) && ! in_array( $user->get_error_code(), $ignore_codes, true ) ) {
715 $error = $user;
716
717 /**
718 * Fires after a user login has failed.
719 *
720 * @since 2.5.0
721 * @since 4.5.0 The value of `$username` can now be an email address.
722 * @since 5.4.0 The `$error` parameter was added.
723 *
724 * @param string $username Username or email address.
725 * @param WP_Error $error A WP_Error object with the authentication failure details.
726 */
727 do_action( 'wp_login_failed', $username, $error );
728 }
729
730 return $user;
731 }
732endif;
733
734if ( ! function_exists( 'wp_logout' ) ) :
735 /**
736 * Logs the current user out.
737 *
738 * @since 2.5.0
739 */
740 function wp_logout() {
741 $user_id = get_current_user_id();
742
743 wp_destroy_current_session();
744 wp_clear_auth_cookie();
745 wp_set_current_user( 0 );
746
747 /**
748 * Fires after a user is logged out.
749 *
750 * @since 1.5.0
751 * @since 5.5.0 Added the `$user_id` parameter.
752 *
753 * @param int $user_id ID of the user that was logged out.
754 */
755 do_action( 'wp_logout', $user_id );
756 }
757endif;
758
759if ( ! function_exists( 'wp_validate_auth_cookie' ) ) :
760 /**
761 * Validates authentication cookie.
762 *
763 * The checks include making sure that the authentication cookie is set and
764 * pulling in the contents (if $cookie is not used).
765 *
766 * Makes sure the cookie is not expired. Verifies the hash in cookie is what is
767 * should be and compares the two.
768 *
769 * @since 2.5.0
770 *
771 * @global int $login_grace_period
772 *
773 * @param string $cookie Optional. If used, will validate contents instead of cookie's.
774 * @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'.
775 * Note: This does *not* default to 'auth' like other cookie functions.
776 * @return int|false User ID if valid cookie, false if invalid.
777 */
778 function wp_validate_auth_cookie( $cookie = '', $scheme = '' ) {
779 $cookie_elements = wp_parse_auth_cookie( $cookie, $scheme );
780 if ( ! $cookie_elements ) {
781 /**
782 * Fires if an authentication cookie is malformed.
783 *
784 * @since 2.7.0
785 *
786 * @param string $cookie Malformed auth cookie.
787 * @param string $scheme Authentication scheme. Values include 'auth', 'secure_auth',
788 * or 'logged_in'.
789 */
790 do_action( 'auth_cookie_malformed', $cookie, $scheme );
791 return false;
792 }
793
794 $scheme = $cookie_elements['scheme'];
795 $username = $cookie_elements['username'];
796 $hmac = $cookie_elements['hmac'];
797 $token = $cookie_elements['token'];
798 $expiration = $cookie_elements['expiration'];
799
800 $expired = (int) $expiration;
801
802 // Allow a grace period for POST and Ajax requests.
803 if ( wp_doing_ajax() || 'POST' === $_SERVER['REQUEST_METHOD'] ) {
804 $expired += HOUR_IN_SECONDS;
805 }
806
807 // Quick check to see if an honest cookie has expired.
808 if ( $expired < time() ) {
809 /**
810 * Fires once an authentication cookie has expired.
811 *
812 * @since 2.7.0
813 *
814 * @param string[] $cookie_elements {
815 * Authentication cookie components. None of the components should be assumed
816 * to be valid as they come directly from a client-provided cookie value.
817 *
818 * @type string $username User's username.
819 * @type string $expiration The time the cookie expires as a UNIX timestamp.
820 * @type string $token User's session token used.
821 * @type string $hmac The security hash for the cookie.
822 * @type string $scheme The cookie scheme to use.
823 * }
824 */
825 do_action( 'auth_cookie_expired', $cookie_elements );
826 return false;
827 }
828
829 $user = get_user_by( 'login', $username );
830 if ( ! $user ) {
831 /**
832 * Fires if a bad username is entered in the user authentication process.
833 *
834 * @since 2.7.0
835 *
836 * @param string[] $cookie_elements {
837 * Authentication cookie components. None of the components should be assumed
838 * to be valid as they come directly from a client-provided cookie value.
839 *
840 * @type string $username User's username.
841 * @type string $expiration The time the cookie expires as a UNIX timestamp.
842 * @type string $token User's session token used.
843 * @type string $hmac The security hash for the cookie.
844 * @type string $scheme The cookie scheme to use.
845 * }
846 */
847 do_action( 'auth_cookie_bad_username', $cookie_elements );
848 return false;
849 }
850
851 if ( str_starts_with( $user->user_pass, '$P$' ) || str_starts_with( $user->user_pass, '$2y$' ) ) {
852 // Retain previous behaviour of phpass or vanilla bcrypt hashed passwords.
853 $pass_frag = substr( $user->user_pass, 8, 4 );
854 } else {
855 // Otherwise, use a substring from the end of the hash to avoid dealing with potentially long hash prefixes.
856 $pass_frag = substr( $user->user_pass, -4 );
857 }
858
859 $key = wp_hash( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
860
861 $hash = hash_hmac( 'sha256', $username . '|' . $expiration . '|' . $token, $key );
862
863 if ( ! hash_equals( $hash, $hmac ) ) {
864 /**
865 * Fires if a bad authentication cookie hash is encountered.
866 *
867 * @since 2.7.0
868 *
869 * @param string[] $cookie_elements {
870 * Authentication cookie components. None of the components should be assumed
871 * to be valid as they come directly from a client-provided cookie value.
872 *
873 * @type string $username User's username.
874 * @type string $expiration The time the cookie expires as a UNIX timestamp.
875 * @type string $token User's session token used.
876 * @type string $hmac The security hash for the cookie.
877 * @type string $scheme The cookie scheme to use.
878 * }
879 */
880 do_action( 'auth_cookie_bad_hash', $cookie_elements );
881 return false;
882 }
883
884 $manager = WP_Session_Tokens::get_instance( $user->ID );
885 if ( ! $manager->verify( $token ) ) {
886 /**
887 * Fires if a bad session token is encountered.
888 *
889 * @since 4.0.0
890 *
891 * @param string[] $cookie_elements {
892 * Authentication cookie components. None of the components should be assumed
893 * to be valid as they come directly from a client-provided cookie value.
894 *
895 * @type string $username User's username.
896 * @type string $expiration The time the cookie expires as a UNIX timestamp.
897 * @type string $token User's session token used.
898 * @type string $hmac The security hash for the cookie.
899 * @type string $scheme The cookie scheme to use.
900 * }
901 */
902 do_action( 'auth_cookie_bad_session_token', $cookie_elements );
903 return false;
904 }
905
906 // Ajax/POST grace period set above.
907 if ( $expiration < time() ) {
908 $GLOBALS['login_grace_period'] = 1;
909 }
910
911 /**
912 * Fires once an authentication cookie has been validated.
913 *
914 * @since 2.7.0
915 *
916 * @param string[] $cookie_elements {
917 * Authentication cookie components.
918 *
919 * @type string $username User's username.
920 * @type string $expiration The time the cookie expires as a UNIX timestamp.
921 * @type string $token User's session token used.
922 * @type string $hmac The security hash for the cookie.
923 * @type string $scheme The cookie scheme to use.
924 * }
925 * @param WP_User $user User object.
926 */
927 do_action( 'auth_cookie_valid', $cookie_elements, $user );
928
929 return $user->ID;
930 }
931endif;
932
933if ( ! function_exists( 'wp_generate_auth_cookie' ) ) :
934 /**
935 * Generates authentication cookie contents.
936 *
937 * @since 2.5.0
938 * @since 4.0.0 The `$token` parameter was added.
939 *
940 * @param int $user_id User ID.
941 * @param int $expiration The time the cookie expires as a UNIX timestamp.
942 * @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'.
943 * Default 'auth'.
944 * @param string $token User's session token to use for this cookie.
945 * @return string Authentication cookie contents. Empty string if user does not exist.
946 */
947 function wp_generate_auth_cookie( $user_id, $expiration, $scheme = 'auth', $token = '' ) {
948 $user = get_userdata( $user_id );
949 if ( ! $user ) {
950 return '';
951 }
952
953 if ( ! $token ) {
954 $manager = WP_Session_Tokens::get_instance( $user_id );
955 $token = $manager->create( $expiration );
956 }
957
958 if ( str_starts_with( $user->user_pass, '$P$' ) || str_starts_with( $user->user_pass, '$2y$' ) ) {
959 // Retain previous behaviour of phpass or vanilla bcrypt hashed passwords.
960 $pass_frag = substr( $user->user_pass, 8, 4 );
961 } else {
962 // Otherwise, use a substring from the end of the hash to avoid dealing with potentially long hash prefixes.
963 $pass_frag = substr( $user->user_pass, -4 );
964 }
965
966 $key = wp_hash( $user->user_login . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
967
968 $hash = hash_hmac( 'sha256', $user->user_login . '|' . $expiration . '|' . $token, $key );
969
970 $cookie = $user->user_login . '|' . $expiration . '|' . $token . '|' . $hash;
971
972 /**
973 * Filters the authentication cookie.
974 *
975 * @since 2.5.0
976 * @since 4.0.0 The `$token` parameter was added.
977 *
978 * @param string $cookie Authentication cookie.
979 * @param int $user_id User ID.
980 * @param int $expiration The time the cookie expires as a UNIX timestamp.
981 * @param string $scheme Cookie scheme used. Accepts 'auth', 'secure_auth', or 'logged_in'.
982 * @param string $token User's session token used.
983 */
984 return apply_filters( 'auth_cookie', $cookie, $user_id, $expiration, $scheme, $token );
985 }
986endif;
987
988if ( ! function_exists( 'wp_parse_auth_cookie' ) ) :
989 /**
990 * Parses a cookie into its components.
991 *
992 * @since 2.7.0
993 * @since 4.0.0 The `$token` element was added to the return value.
994 *
995 * @param string $cookie Authentication cookie.
996 * @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'.
997 * @return string[]|false {
998 * Authentication cookie components. None of the components should be assumed
999 * to be valid as they come directly from a client-provided cookie value. If
1000 * the cookie value is malformed, false is returned.
1001 *
1002 * @type string $username User's username.
1003 * @type string $expiration The time the cookie expires as a UNIX timestamp.
1004 * @type string $token User's session token used.
1005 * @type string $hmac The security hash for the cookie.
1006 * @type string $scheme The cookie scheme to use.
1007 * }
1008 */
1009 function wp_parse_auth_cookie( $cookie = '', $scheme = '' ) {
1010 if ( empty( $cookie ) ) {
1011 switch ( $scheme ) {
1012 case 'auth':
1013 $cookie_name = AUTH_COOKIE;
1014 break;
1015 case 'secure_auth':
1016 $cookie_name = SECURE_AUTH_COOKIE;
1017 break;
1018 case 'logged_in':
1019 $cookie_name = LOGGED_IN_COOKIE;
1020 break;
1021 default:
1022 if ( is_ssl() ) {
1023 $cookie_name = SECURE_AUTH_COOKIE;
1024 $scheme = 'secure_auth';
1025 } else {
1026 $cookie_name = AUTH_COOKIE;
1027 $scheme = 'auth';
1028 }
1029 }
1030
1031 if ( empty( $_COOKIE[ $cookie_name ] ) ) {
1032 return false;
1033 }
1034 $cookie = $_COOKIE[ $cookie_name ];
1035 }
1036
1037 $cookie_elements = explode( '|', $cookie );
1038 if ( count( $cookie_elements ) !== 4 ) {
1039 return false;
1040 }
1041
1042 list( $username, $expiration, $token, $hmac ) = $cookie_elements;
1043
1044 return compact( 'username', 'expiration', 'token', 'hmac', 'scheme' );
1045 }
1046endif;
1047
1048if ( ! function_exists( 'wp_set_auth_cookie' ) ) :
1049 /**
1050 * Sets the authentication cookies for a given user ID.
1051 *
1052 * The `$remember` parameter controls cookie persistence:
1053 * - If true, the cookie is persistent (default 14 days, filterable via {@see 'auth_cookie_expiration'}).
1054 * - If false, the cookie is a browser session cookie (expires when the browser closes).
1055 * Internally, {@see 'auth_cookie_expiration'} is still applied, to expire the login after
1056 * two days or when the browser is closed, whichever occurs first.
1057 *
1058 * @since 2.5.0
1059 * @since 4.3.0 Added the `$token` parameter.
1060 *
1061 * @param int $user_id User ID.
1062 * @param bool $remember Whether to remember the user.
1063 * @param bool|string $secure Whether the auth cookie should only be sent over HTTPS. Default is an empty
1064 * string which means the value of `is_ssl()` will be used.
1065 * @param string $token Optional. User's session token to use for this cookie.
1066 */
1067 function wp_set_auth_cookie( $user_id, $remember = false, $secure = '', $token = '' ) {
1068 if ( $remember ) {
1069 /**
1070 * Filters the duration of the authentication cookie expiration period.
1071 *
1072 * @since 2.8.0
1073 *
1074 * @param int $length Duration of the expiration period in seconds.
1075 * @param int $user_id User ID.
1076 * @param bool $remember Whether to remember the user login. Default false.
1077 */
1078 $expiration = time() + apply_filters( 'auth_cookie_expiration', 14 * DAY_IN_SECONDS, $user_id, $remember );
1079
1080 /*
1081 * Ensure the browser will continue to send the cookie after the expiration time is reached.
1082 * Needed for the login grace period in wp_validate_auth_cookie().
1083 */
1084 $expire = $expiration + ( 12 * HOUR_IN_SECONDS );
1085 } else {
1086 /** This filter is documented in wp-includes/pluggable.php */
1087 $expiration = time() + apply_filters( 'auth_cookie_expiration', 2 * DAY_IN_SECONDS, $user_id, $remember );
1088 $expire = 0;
1089 }
1090
1091 if ( '' === $secure ) {
1092 $secure = is_ssl();
1093 }
1094
1095 // Front-end cookie is secure when the auth cookie is secure and the site's home URL uses HTTPS.
1096 $secure_logged_in_cookie = $secure && 'https' === parse_url( get_option( 'home' ), PHP_URL_SCHEME );
1097
1098 /**
1099 * Filters whether the auth cookie should only be sent over HTTPS.
1100 *
1101 * @since 3.1.0
1102 *
1103 * @param bool $secure Whether the cookie should only be sent over HTTPS.
1104 * @param int $user_id User ID.
1105 */
1106 $secure = apply_filters( 'secure_auth_cookie', $secure, $user_id );
1107
1108 /**
1109 * Filters whether the logged in cookie should only be sent over HTTPS.
1110 *
1111 * @since 3.1.0
1112 *
1113 * @param bool $secure_logged_in_cookie Whether the logged in cookie should only be sent over HTTPS.
1114 * @param int $user_id User ID.
1115 * @param bool $secure Whether the auth cookie should only be sent over HTTPS.
1116 */
1117 $secure_logged_in_cookie = apply_filters( 'secure_logged_in_cookie', $secure_logged_in_cookie, $user_id, $secure );
1118
1119 if ( $secure ) {
1120 $auth_cookie_name = SECURE_AUTH_COOKIE;
1121 $scheme = 'secure_auth';
1122 } else {
1123 $auth_cookie_name = AUTH_COOKIE;
1124 $scheme = 'auth';
1125 }
1126
1127 if ( '' === $token ) {
1128 $manager = WP_Session_Tokens::get_instance( $user_id );
1129 $token = $manager->create( $expiration );
1130 }
1131
1132 $auth_cookie = wp_generate_auth_cookie( $user_id, $expiration, $scheme, $token );
1133 $logged_in_cookie = wp_generate_auth_cookie( $user_id, $expiration, 'logged_in', $token );
1134
1135 /**
1136 * Fires immediately before the authentication cookie is set.
1137 *
1138 * @since 2.5.0
1139 * @since 4.9.0 The `$token` parameter was added.
1140 *
1141 * @param string $auth_cookie Authentication cookie value.
1142 * @param int $expire The time the login grace period expires as a UNIX timestamp.
1143 * Default is 12 hours past the cookie's expiration time.
1144 * @param int $expiration The time when the authentication cookie expires as a UNIX timestamp.
1145 * Default is 14 days from now.
1146 * @param int $user_id User ID.
1147 * @param string $scheme Authentication scheme. Values include 'auth' or 'secure_auth'.
1148 * @param string $token User's session token to use for this cookie.
1149 */
1150 do_action( 'set_auth_cookie', $auth_cookie, $expire, $expiration, $user_id, $scheme, $token );
1151
1152 /**
1153 * Fires immediately before the logged-in authentication cookie is set.
1154 *
1155 * @since 2.6.0
1156 * @since 4.9.0 The `$token` parameter was added.
1157 *
1158 * @param string $logged_in_cookie The logged-in cookie value.
1159 * @param int $expire The time the login grace period expires as a UNIX timestamp.
1160 * Default is 12 hours past the cookie's expiration time.
1161 * @param int $expiration The time when the logged-in authentication cookie expires as a UNIX timestamp.
1162 * Default is 14 days from now.
1163 * @param int $user_id User ID.
1164 * @param string $scheme Authentication scheme. Default 'logged_in'.
1165 * @param string $token User's session token to use for this cookie.
1166 */
1167 do_action( 'set_logged_in_cookie', $logged_in_cookie, $expire, $expiration, $user_id, 'logged_in', $token );
1168
1169 /**
1170 * Allows preventing auth cookies from actually being sent to the client.
1171 *
1172 * @since 4.7.4
1173 * @since 6.2.0 The `$expire`, `$expiration`, `$user_id`, `$scheme`, and `$token` parameters were added.
1174 *
1175 * @param bool $send Whether to send auth cookies to the client. Default true.
1176 * @param int $expire The time the login grace period expires as a UNIX timestamp.
1177 * Default is 12 hours past the cookie's expiration time. Zero when clearing cookies.
1178 * @param int $expiration The time when the logged-in authentication cookie expires as a UNIX timestamp.
1179 * Default is 14 days from now. Zero when clearing cookies.
1180 * @param int $user_id User ID. Zero when clearing cookies.
1181 * @param string $scheme Authentication scheme. Values include 'auth' or 'secure_auth'.
1182 * Empty string when clearing cookies.
1183 * @param string $token User's session token to use for this cookie. Empty string when clearing cookies.
1184 */
1185 if ( ! apply_filters( 'send_auth_cookies', true, $expire, $expiration, $user_id, $scheme, $token ) ) {
1186 return;
1187 }
1188
1189 setcookie( $auth_cookie_name, $auth_cookie, $expire, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN, $secure, true );
1190 setcookie( $auth_cookie_name, $auth_cookie, $expire, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, $secure, true );
1191 setcookie( LOGGED_IN_COOKIE, $logged_in_cookie, $expire, COOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true );
1192 if ( COOKIEPATH !== SITECOOKIEPATH ) {
1193 setcookie( LOGGED_IN_COOKIE, $logged_in_cookie, $expire, SITECOOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true );
1194 }
1195 }
1196endif;
1197
1198if ( ! function_exists( 'wp_clear_auth_cookie' ) ) :
1199 /**
1200 * Removes all of the cookies associated with authentication.
1201 *
1202 * @since 2.5.0
1203 */
1204 function wp_clear_auth_cookie() {
1205 /**
1206 * Fires just before the authentication cookies are cleared.
1207 *
1208 * @since 2.7.0
1209 */
1210 do_action( 'clear_auth_cookie' );
1211
1212 /** This filter is documented in wp-includes/pluggable.php */
1213 if ( ! apply_filters( 'send_auth_cookies', true, 0, 0, 0, '', '' ) ) {
1214 return;
1215 }
1216
1217 // Auth cookies.
1218 setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN );
1219 setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, ADMIN_COOKIE_PATH, COOKIE_DOMAIN );
1220 setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN );
1221 setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN );
1222 setcookie( LOGGED_IN_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
1223 setcookie( LOGGED_IN_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
1224
1225 // Settings cookies.
1226 setcookie( 'wp-settings-' . get_current_user_id(), ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH );
1227 setcookie( 'wp-settings-time-' . get_current_user_id(), ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH );
1228
1229 // Old cookies.
1230 setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
1231 setcookie( AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
1232 setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
1233 setcookie( SECURE_AUTH_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
1234
1235 // Even older cookies.
1236 setcookie( USER_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
1237 setcookie( PASS_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
1238 setcookie( USER_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
1239 setcookie( PASS_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
1240
1241 // Post password cookie.
1242 setcookie( 'wp-postpass_' . COOKIEHASH, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
1243 }
1244endif;
1245
1246if ( ! function_exists( 'is_user_logged_in' ) ) :
1247 /**
1248 * Determines whether the current visitor is a logged in user.
1249 *
1250 * For more information on this and similar theme functions, check out
1251 * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
1252 * Conditional Tags} article in the Theme Developer Handbook.
1253 *
1254 * @since 2.0.0
1255 *
1256 * @return bool True if user is logged in, false if not logged in.
1257 */
1258 function is_user_logged_in() {
1259 $user = wp_get_current_user();
1260
1261 return $user->exists();
1262 }
1263endif;
1264
1265if ( ! function_exists( 'auth_redirect' ) ) :
1266 /**
1267 * Checks if a user is logged in, if not it redirects them to the login page.
1268 *
1269 * When this code is called from a page, it checks to see if the user viewing the page is logged in.
1270 * If the user is not logged in, they are redirected to the login page. The user is redirected
1271 * in such a way that, upon logging in, they will be sent directly to the page they were originally
1272 * trying to access.
1273 *
1274 * @since 1.5.0
1275 */
1276 function auth_redirect() {
1277 $secure = ( is_ssl() || force_ssl_admin() );
1278
1279 /**
1280 * Filters whether to use a secure authentication redirect.
1281 *
1282 * @since 3.1.0
1283 *
1284 * @param bool $secure Whether to use a secure authentication redirect. Default false.
1285 */
1286 $secure = apply_filters( 'secure_auth_redirect', $secure );
1287
1288 // If https is required and request is http, redirect.
1289 if ( $secure && ! is_ssl() && str_contains( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) {
1290 if ( str_starts_with( $_SERVER['REQUEST_URI'], 'http' ) ) {
1291 wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) );
1292 exit;
1293 } else {
1294 wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1295 exit;
1296 }
1297 }
1298
1299 /**
1300 * Filters the authentication redirect scheme.
1301 *
1302 * @since 2.9.0
1303 *
1304 * @param string $scheme Authentication redirect scheme. Default empty.
1305 */
1306 $scheme = apply_filters( 'auth_redirect_scheme', '' );
1307
1308 $user_id = wp_validate_auth_cookie( '', $scheme );
1309 if ( $user_id ) {
1310 /**
1311 * Fires before the authentication redirect.
1312 *
1313 * @since 2.8.0
1314 *
1315 * @param int $user_id User ID.
1316 */
1317 do_action( 'auth_redirect', $user_id );
1318
1319 // If the user wants ssl but the session is not ssl, redirect.
1320 if ( ! $secure && get_user_option( 'use_ssl', $user_id ) && str_contains( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) {
1321 if ( str_starts_with( $_SERVER['REQUEST_URI'], 'http' ) ) {
1322 wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) );
1323 exit;
1324 } else {
1325 wp_redirect( 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1326 exit;
1327 }
1328 }
1329
1330 return; // The cookie is good, so we're done.
1331 }
1332
1333 // The cookie is no good, so force login.
1334 nocache_headers();
1335
1336 if ( str_contains( $_SERVER['REQUEST_URI'], '/options.php' ) && wp_get_referer() ) {
1337 $redirect = wp_get_referer();
1338 } else {
1339 $redirect = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1340 }
1341
1342 $login_url = wp_login_url( $redirect, true );
1343
1344 wp_redirect( $login_url );
1345 exit;
1346 }
1347endif;
1348
1349if ( ! function_exists( 'check_admin_referer' ) ) :
1350 /**
1351 * Ensures intent by verifying that a user was referred from another admin page with the correct security nonce.
1352 *
1353 * This function ensures the user intends to perform a given action, which helps protect against clickjacking style
1354 * attacks. It verifies intent, not authorization, therefore it does not verify the user's capabilities. This should
1355 * be performed with `current_user_can()` or similar.
1356 *
1357 * If the nonce value is invalid, the function will exit with an "Are You Sure?" style message.
1358 *
1359 * @since 1.2.0
1360 * @since 2.5.0 The `$query_arg` parameter was added.
1361 *
1362 * @param int|string $action The nonce action.
1363 * @param string $query_arg Optional. Key to check for nonce in `$_REQUEST`. Default '_wpnonce'.
1364 * @return int|false 1 if the nonce is valid and generated between 0-12 hours ago,
1365 * 2 if the nonce is valid and generated between 12-24 hours ago.
1366 * False if the nonce is invalid.
1367 */
1368 function check_admin_referer( $action = -1, $query_arg = '_wpnonce' ) {
1369 if ( -1 === $action ) {
1370 _doing_it_wrong( __FUNCTION__, __( 'You should specify an action to be verified by using the first parameter.' ), '3.2.0' );
1371 }
1372
1373 $adminurl = strtolower( admin_url() );
1374 $referer = strtolower( wp_get_referer() );
1375 $result = isset( $_REQUEST[ $query_arg ] ) ? wp_verify_nonce( $_REQUEST[ $query_arg ], $action ) : false;
1376
1377 /**
1378 * Fires once the admin request has been validated or not.
1379 *
1380 * @since 1.5.1
1381 *
1382 * @param string $action The nonce action.
1383 * @param false|int $result False if the nonce is invalid, 1 if the nonce is valid and generated between
1384 * 0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago.
1385 */
1386 do_action( 'check_admin_referer', $action, $result );
1387
1388 if ( ! $result && ! ( -1 === $action && str_starts_with( $referer, $adminurl ) ) ) {
1389 wp_nonce_ays( $action );
1390 die();
1391 }
1392
1393 return $result;
1394 }
1395endif;
1396
1397if ( ! function_exists( 'check_ajax_referer' ) ) :
1398 /**
1399 * Verifies the Ajax request to prevent processing requests external of the blog.
1400 *
1401 * @since 2.0.3
1402 *
1403 * @param int|string $action Action nonce.
1404 * @param false|string $query_arg Optional. Key to check for the nonce in `$_REQUEST` (since 2.5). If false,
1405 * `$_REQUEST` values will be evaluated for '_ajax_nonce', and '_wpnonce'
1406 * (in that order). Default false.
1407 * @param bool $stop Optional. Whether to stop early when the nonce cannot be verified.
1408 * Default true.
1409 * @return int|false 1 if the nonce is valid and generated between 0-12 hours ago,
1410 * 2 if the nonce is valid and generated between 12-24 hours ago.
1411 * False if the nonce is invalid.
1412 */
1413 function check_ajax_referer( $action = -1, $query_arg = false, $stop = true ) {
1414 if ( -1 === $action ) {
1415 _doing_it_wrong( __FUNCTION__, __( 'You should specify an action to be verified by using the first parameter.' ), '4.7.0' );
1416 }
1417
1418 $nonce = '';
1419
1420 if ( $query_arg && isset( $_REQUEST[ $query_arg ] ) ) {
1421 $nonce = $_REQUEST[ $query_arg ];
1422 } elseif ( isset( $_REQUEST['_ajax_nonce'] ) ) {
1423 $nonce = $_REQUEST['_ajax_nonce'];
1424 } elseif ( isset( $_REQUEST['_wpnonce'] ) ) {
1425 $nonce = $_REQUEST['_wpnonce'];
1426 }
1427
1428 $result = wp_verify_nonce( $nonce, $action );
1429
1430 /**
1431 * Fires once the Ajax request has been validated or not.
1432 *
1433 * @since 2.1.0
1434 *
1435 * @param string $action The Ajax nonce action.
1436 * @param false|int $result False if the nonce is invalid, 1 if the nonce is valid and generated between
1437 * 0-12 hours ago, 2 if the nonce is valid and generated between 12-24 hours ago.
1438 */
1439 do_action( 'check_ajax_referer', $action, $result );
1440
1441 if ( $stop && false === $result ) {
1442 if ( wp_doing_ajax() ) {
1443 wp_die( -1, 403 );
1444 } else {
1445 die( '-1' );
1446 }
1447 }
1448
1449 return $result;
1450 }
1451endif;
1452
1453if ( ! function_exists( 'wp_redirect' ) ) :
1454 /**
1455 * Redirects to another page.
1456 *
1457 * Note: wp_redirect() does not exit automatically, and should almost always be
1458 * followed by a call to `exit;`:
1459 *
1460 * wp_redirect( $url );
1461 * exit;
1462 *
1463 * Exiting can also be selectively manipulated by using wp_redirect() as a conditional
1464 * in conjunction with the {@see 'wp_redirect'} and {@see 'wp_redirect_status'} filters:
1465 *
1466 * if ( wp_redirect( $url ) ) {
1467 * exit;
1468 * }
1469 *
1470 * @since 1.5.1
1471 * @since 5.1.0 The `$x_redirect_by` parameter was added.
1472 * @since 5.4.0 On invalid status codes, wp_die() is called.
1473 *
1474 * @global bool $is_IIS
1475 *
1476 * @param string $location The path or URL to redirect to.
1477 * @param int $status Optional. HTTP response status code to use. Default '302' (Moved Temporarily).
1478 * @param string|false $x_redirect_by Optional. The application doing the redirect or false to omit. Default 'WordPress'.
1479 * @return bool False if the redirect was canceled, true otherwise.
1480 */
1481 function wp_redirect( $location, $status = 302, $x_redirect_by = 'WordPress' ) {
1482 global $is_IIS;
1483
1484 /**
1485 * Filters the redirect location.
1486 *
1487 * @since 2.1.0
1488 *
1489 * @param string $location The path or URL to redirect to.
1490 * @param int $status The HTTP response status code to use.
1491 */
1492 $location = apply_filters( 'wp_redirect', $location, $status );
1493
1494 /**
1495 * Filters the redirect HTTP response status code to use.
1496 *
1497 * @since 2.3.0
1498 *
1499 * @param int $status The HTTP response status code to use.
1500 * @param string $location The path or URL to redirect to.
1501 */
1502 $status = apply_filters( 'wp_redirect_status', $status, $location );
1503
1504 if ( ! $location ) {
1505 return false;
1506 }
1507
1508 if ( $status < 300 || 399 < $status ) {
1509 wp_die( __( 'HTTP redirect status code must be a redirection code, 3xx.' ) );
1510 }
1511
1512 $location = wp_sanitize_redirect( $location );
1513
1514 if ( ! $is_IIS && 'cgi-fcgi' !== PHP_SAPI ) {
1515 status_header( $status ); // This causes problems on IIS and some FastCGI setups.
1516 }
1517
1518 /**
1519 * Filters the X-Redirect-By header.
1520 *
1521 * Allows applications to identify themselves when they're doing a redirect.
1522 *
1523 * @since 5.1.0
1524 *
1525 * @param string|false $x_redirect_by The application doing the redirect or false to omit the header.
1526 * @param int $status Status code to use.
1527 * @param string $location The path to redirect to.
1528 */
1529 $x_redirect_by = apply_filters( 'x_redirect_by', $x_redirect_by, $status, $location );
1530 if ( is_string( $x_redirect_by ) ) {
1531 header( "X-Redirect-By: $x_redirect_by" );
1532 }
1533
1534 header( "Location: $location", true, $status );
1535
1536 return true;
1537 }
1538endif;
1539
1540if ( ! function_exists( 'wp_sanitize_redirect' ) ) :
1541 /**
1542 * Sanitizes a URL for use in a redirect.
1543 *
1544 * @since 2.3.0
1545 *
1546 * @param string $location The path to redirect to.
1547 * @return string Redirect-sanitized URL.
1548 */
1549 function wp_sanitize_redirect( $location ) {
1550 // Encode spaces.
1551 $location = str_replace( ' ', '%20', $location );
1552
1553 $regex = '/
1554 (
1555 (?: [\xC2-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx
1556 | \xE0[\xA0-\xBF][\x80-\xBF] # triple-byte sequences 1110xxxx 10xxxxxx * 2
1557 | [\xE1-\xEC][\x80-\xBF]{2}
1558 | \xED[\x80-\x9F][\x80-\xBF]
1559 | [\xEE-\xEF][\x80-\xBF]{2}
1560 | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences 11110xxx 10xxxxxx * 3
1561 | [\xF1-\xF3][\x80-\xBF]{3}
1562 | \xF4[\x80-\x8F][\x80-\xBF]{2}
1563 ){1,40} # ...one or more times
1564 )/x';
1565 $location = preg_replace_callback( $regex, '_wp_sanitize_utf8_in_redirect', $location );
1566 $location = preg_replace( '|[^a-z0-9-~+_.?#=&;,/:%!*\[\]()@]|i', '', $location );
1567 $location = wp_kses_no_null( $location );
1568
1569 // Remove %0D and %0A from location.
1570 $strip = array( '%0d', '%0a', '%0D', '%0A' );
1571 return _deep_replace( $strip, $location );
1572 }
1573
1574 /**
1575 * URL encodes UTF-8 characters in a URL.
1576 *
1577 * @ignore
1578 * @since 4.2.0
1579 * @access private
1580 *
1581 * @see wp_sanitize_redirect()
1582 *
1583 * @param array $matches RegEx matches against the redirect location.
1584 * @return string URL-encoded version of the first RegEx match.
1585 */
1586 function _wp_sanitize_utf8_in_redirect( $matches ) {
1587 return urlencode( $matches[0] );
1588 }
1589endif;
1590
1591if ( ! function_exists( 'wp_safe_redirect' ) ) :
1592 /**
1593 * Performs a safe (local) redirect, using wp_redirect().
1594 *
1595 * Checks whether the $location is using an allowed host, if it has an absolute
1596 * path. A plugin can therefore set or remove allowed host(s) to or from the
1597 * list.
1598 *
1599 * If the host is not allowed, then the redirect defaults to wp-admin on the siteurl
1600 * instead. This prevents malicious redirects which redirect to another host,
1601 * but only used in a few places.
1602 *
1603 * Note: wp_safe_redirect() does not exit automatically, and should almost always be
1604 * followed by a call to `exit;`:
1605 *
1606 * wp_safe_redirect( $url );
1607 * exit;
1608 *
1609 * Exiting can also be selectively manipulated by using wp_safe_redirect() as a conditional
1610 * in conjunction with the {@see 'wp_redirect'} and {@see 'wp_redirect_status'} filters:
1611 *
1612 * if ( wp_safe_redirect( $url ) ) {
1613 * exit;
1614 * }
1615 *
1616 * @since 2.3.0
1617 * @since 5.1.0 The return value from wp_redirect() is now passed on, and the `$x_redirect_by` parameter was added.
1618 *
1619 * @param string $location The path or URL to redirect to.
1620 * @param int $status Optional. HTTP response status code to use. Default '302' (Moved Temporarily).
1621 * @param string|false $x_redirect_by Optional. The application doing the redirect or false to omit. Default 'WordPress'.
1622 * @return bool False if the redirect was canceled, true otherwise.
1623 */
1624 function wp_safe_redirect( $location, $status = 302, $x_redirect_by = 'WordPress' ) {
1625
1626 // Need to look at the URL the way it will end up in wp_redirect().
1627 $location = wp_sanitize_redirect( $location );
1628
1629 /**
1630 * Filters the redirect fallback URL for when the provided redirect is not safe (local).
1631 *
1632 * @since 4.3.0
1633 *
1634 * @param string $fallback_url The fallback URL to use by default.
1635 * @param int $status The HTTP response status code to use.
1636 */
1637 $fallback_url = apply_filters( 'wp_safe_redirect_fallback', admin_url(), $status );
1638
1639 $location = wp_validate_redirect( $location, $fallback_url );
1640
1641 return wp_redirect( $location, $status, $x_redirect_by );
1642 }
1643endif;
1644
1645if ( ! function_exists( 'wp_validate_redirect' ) ) :
1646 /**
1647 * Validates a URL for use in a redirect.
1648 *
1649 * Checks whether the $location is using an allowed host, if it has an absolute
1650 * path. A plugin can therefore set or remove allowed host(s) to or from the
1651 * list.
1652 *
1653 * If the host is not allowed, then the redirect is to $fallback_url supplied.
1654 *
1655 * @since 2.8.1
1656 *
1657 * @param string $location The redirect to validate.
1658 * @param string $fallback_url The value to return if $location is not allowed.
1659 * @return string Redirect-sanitized URL.
1660 */
1661 function wp_validate_redirect( $location, $fallback_url = '' ) {
1662 $location = wp_sanitize_redirect( trim( $location, " \t\n\r\0\x08\x0B" ) );
1663 // Browsers will assume 'http' is your protocol, and will obey a redirect to a URL starting with '//'.
1664 if ( str_starts_with( $location, '//' ) ) {
1665 $location = 'http:' . $location;
1666 }
1667
1668 /*
1669 * In PHP 5 parse_url() may fail if the URL query part contains 'http://'.
1670 * See https://bugs.php.net/bug.php?id=38143
1671 */
1672 $cut = strpos( $location, '?' );
1673 $test = $cut ? substr( $location, 0, $cut ) : $location;
1674
1675 $lp = parse_url( $test );
1676
1677 // Give up if malformed URL.
1678 if ( false === $lp ) {
1679 return $fallback_url;
1680 }
1681
1682 // Allow only 'http' and 'https' schemes. No 'data:', etc.
1683 if ( isset( $lp['scheme'] ) && ! ( 'http' === $lp['scheme'] || 'https' === $lp['scheme'] ) ) {
1684 return $fallback_url;
1685 }
1686
1687 if ( ! isset( $lp['host'] ) && ! empty( $lp['path'] ) && '/' !== $lp['path'][0] ) {
1688 $path = '';
1689 if ( ! empty( $_SERVER['REQUEST_URI'] ) ) {
1690 $path = dirname( parse_url( 'http://placeholder' . $_SERVER['REQUEST_URI'], PHP_URL_PATH ) . '?' );
1691 $path = wp_normalize_path( $path );
1692 }
1693 $location = '/' . ltrim( $path . '/', '/' ) . $location;
1694 }
1695
1696 /*
1697 * Reject if certain components are set but host is not.
1698 * This catches URLs like https:host.com for which parse_url() does not set the host field.
1699 */
1700 if ( ! isset( $lp['host'] ) && ( isset( $lp['scheme'] ) || isset( $lp['user'] ) || isset( $lp['pass'] ) || isset( $lp['port'] ) ) ) {
1701 return $fallback_url;
1702 }
1703
1704 // Reject malformed components parse_url() can return on odd inputs.
1705 foreach ( array( 'user', 'pass', 'host' ) as $component ) {
1706 if ( isset( $lp[ $component ] ) && strpbrk( $lp[ $component ], ':/?#@' ) ) {
1707 return $fallback_url;
1708 }
1709 }
1710
1711 $wpp = parse_url( home_url() );
1712
1713 /**
1714 * Filters the list of allowed hosts to redirect to.
1715 *
1716 * @since 2.3.0
1717 *
1718 * @param string[] $hosts An array of allowed host names.
1719 * @param string $host The host name of the redirect destination; empty string if not set.
1720 */
1721 $allowed_hosts = (array) apply_filters( 'allowed_redirect_hosts', array( $wpp['host'] ), isset( $lp['host'] ) ? $lp['host'] : '' );
1722
1723 if ( isset( $lp['host'] ) && ( ! in_array( $lp['host'], $allowed_hosts, true ) && strtolower( $wpp['host'] ) !== $lp['host'] ) ) {
1724 $location = $fallback_url;
1725 }
1726
1727 return $location;
1728 }
1729endif;
1730
1731if ( ! function_exists( 'wp_notify_postauthor' ) ) :
1732 /**
1733 * Notifies an author (and/or others) of a comment/trackback/pingback on a post.
1734 *
1735 * @since 1.0.0
1736 *
1737 * @param int|WP_Comment $comment_id Comment ID or WP_Comment object.
1738 * @param string $deprecated Not used.
1739 * @return bool True on completion. False if no email addresses were specified.
1740 */
1741 function wp_notify_postauthor( $comment_id, $deprecated = null ) {
1742 if ( null !== $deprecated ) {
1743 _deprecated_argument( __FUNCTION__, '3.8.0' );
1744 }
1745
1746 $comment = get_comment( $comment_id );
1747 if ( empty( $comment ) || empty( $comment->comment_post_ID ) ) {
1748 return false;
1749 }
1750
1751 $post = get_post( $comment->comment_post_ID );
1752 $author = get_userdata( $post->post_author );
1753
1754 // Who to notify? By default, just the post author, but others can be added.
1755 $emails = array();
1756 if ( $author ) {
1757 $emails[] = $author->user_email;
1758 }
1759
1760 /**
1761 * Filters the list of email addresses to receive a comment notification.
1762 *
1763 * By default, only post authors are notified of comments. This filter allows
1764 * others to be added.
1765 *
1766 * @since 3.7.0
1767 *
1768 * @param string[] $emails An array of email addresses to receive a comment notification.
1769 * @param string $comment_id The comment ID as a numeric string.
1770 */
1771 $emails = apply_filters( 'comment_notification_recipients', $emails, $comment->comment_ID );
1772 $emails = array_filter( $emails );
1773
1774 // If there are no addresses to send the comment to, bail.
1775 if ( ! count( $emails ) ) {
1776 return false;
1777 }
1778
1779 // Facilitate unsetting below without knowing the keys.
1780 $emails = array_flip( $emails );
1781
1782 /**
1783 * Filters whether to notify comment authors of their comments on their own posts.
1784 *
1785 * By default, comment authors aren't notified of their comments on their own
1786 * posts. This filter allows you to override that.
1787 *
1788 * @since 3.8.0
1789 *
1790 * @param bool $notify Whether to notify the post author of their own comment.
1791 * Default false.
1792 * @param string $comment_id The comment ID as a numeric string.
1793 */
1794 $notify_author = apply_filters( 'comment_notification_notify_author', false, $comment->comment_ID );
1795
1796 // The comment was left by the author.
1797 if ( $author && ! $notify_author && (int) $comment->user_id === (int) $post->post_author ) {
1798 unset( $emails[ $author->user_email ] );
1799 }
1800
1801 // The author moderated a comment on their own post.
1802 if ( $author && ! $notify_author && get_current_user_id() === (int) $post->post_author ) {
1803 unset( $emails[ $author->user_email ] );
1804 }
1805
1806 // The post author is no longer a member of the blog.
1807 if ( $author && ! $notify_author && ! user_can( $post->post_author, 'read_post', $post->ID ) ) {
1808 unset( $emails[ $author->user_email ] );
1809 }
1810
1811 // If there's no email to send the comment to, bail, otherwise flip array back around for use below.
1812 if ( ! count( $emails ) ) {
1813 return false;
1814 } else {
1815 $emails = array_flip( $emails );
1816 }
1817
1818 $comment_author_domain = '';
1819 if ( WP_Http::is_ip_address( $comment->comment_author_IP ) ) {
1820 $comment_author_domain = gethostbyaddr( $comment->comment_author_IP );
1821 }
1822
1823 /*
1824 * The blogname option is escaped with esc_html() on the way into the database in sanitize_option().
1825 * We want to reverse this for the plain text arena of emails.
1826 */
1827 $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
1828 $comment_content = wp_specialchars_decode( $comment->comment_content );
1829
1830 $wp_email = 'wordpress@' . preg_replace( '#^www\.#', '', wp_parse_url( network_home_url(), PHP_URL_HOST ) );
1831
1832 if ( '' === $comment->comment_author ) {
1833 $from = "From: \"$blogname\" <$wp_email>";
1834 if ( '' !== $comment->comment_author_email ) {
1835 $reply_to = "Reply-To: $comment->comment_author_email";
1836 }
1837 } else {
1838 $from = "From: \"$comment->comment_author\" <$wp_email>";
1839 if ( '' !== $comment->comment_author_email ) {
1840 $reply_to = "Reply-To: \"$comment->comment_author_email\" <$comment->comment_author_email>";
1841 }
1842 }
1843
1844 $message_headers = "$from\n"
1845 . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
1846
1847 if ( isset( $reply_to ) ) {
1848 $message_headers .= $reply_to . "\n";
1849 }
1850
1851 /**
1852 * Filters the comment notification email headers.
1853 *
1854 * @since 1.5.2
1855 *
1856 * @param string $message_headers Headers for the comment notification email.
1857 * @param string $comment_id Comment ID as a numeric string.
1858 */
1859 $message_headers = apply_filters( 'comment_notification_headers', $message_headers, $comment->comment_ID );
1860
1861 foreach ( $emails as $email ) {
1862 $user = get_user_by( 'email', $email );
1863
1864 if ( $user ) {
1865 $switched_locale = switch_to_user_locale( $user->ID );
1866 } else {
1867 $switched_locale = switch_to_locale( get_locale() );
1868 }
1869
1870 switch ( $comment->comment_type ) {
1871 case 'trackback':
1872 /* translators: %s: Post title. */
1873 $notify_message = sprintf( __( 'New trackback on your post "%s"' ), $post->post_title ) . "\r\n";
1874 /* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */
1875 $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
1876 /* translators: %s: Trackback/pingback/comment author URL. */
1877 $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
1878 /* translators: %s: Comment text. */
1879 $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
1880 $notify_message .= __( 'You can see all trackbacks on this post here:' ) . "\r\n";
1881 /* translators: Trackback notification email subject. 1: Site title, 2: Post title. */
1882 $subject = sprintf( __( '[%1$s] Trackback: "%2$s"' ), $blogname, $post->post_title );
1883 break;
1884
1885 case 'pingback':
1886 /* translators: %s: Post title. */
1887 $notify_message = sprintf( __( 'New pingback on your post "%s"' ), $post->post_title ) . "\r\n";
1888 /* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */
1889 $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
1890 /* translators: %s: Trackback/pingback/comment author URL. */
1891 $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
1892 /* translators: %s: Comment text. */
1893 $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
1894 $notify_message .= __( 'You can see all pingbacks on this post here:' ) . "\r\n";
1895 /* translators: Pingback notification email subject. 1: Site title, 2: Post title. */
1896 $subject = sprintf( __( '[%1$s] Pingback: "%2$s"' ), $blogname, $post->post_title );
1897 break;
1898
1899 case 'note':
1900 /* translators: %s: Post title. */
1901 $notify_message = sprintf( __( 'New note on your post "%s"' ), $post->post_title ) . "\r\n";
1902 /* translators: 1: Note author's name, 2: Note author's IP address, 3: Note author's hostname. */
1903 $notify_message .= sprintf( __( 'Author: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
1904 /* translators: %s: Note author email. */
1905 $notify_message .= sprintf( __( 'Email: %s' ), $comment->comment_author_email ) . "\r\n";
1906 /* translators: %s: Note text. */
1907 $notify_message .= sprintf( __( 'Note: %s' ), "\r\n" . ( empty( $comment_content ) ? __( 'resolved/reopened' ) : $comment_content ) ) . "\r\n\r\n";
1908 $notify_message .= __( 'You can see all notes on this post here:' ) . "\r\n";
1909 /* translators: Note notification email subject. 1: Site title, 2: Post title. */
1910 $subject = sprintf( __( '[%1$s] Note: "%2$s"' ), $blogname, $post->post_title );
1911 break;
1912
1913 default: // Comments.
1914 /* translators: %s: Post title. */
1915 $notify_message = sprintf( __( 'New comment on your post "%s"' ), $post->post_title ) . "\r\n";
1916 /* translators: 1: Comment author's name, 2: Comment author's IP address, 3: Comment author's hostname. */
1917 $notify_message .= sprintf( __( 'Author: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
1918 /* translators: %s: Comment author email. */
1919 $notify_message .= sprintf( __( 'Email: %s' ), $comment->comment_author_email ) . "\r\n";
1920 /* translators: %s: Trackback/pingback/comment author URL. */
1921 $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
1922
1923 if ( $comment->comment_parent && user_can( $post->post_author, 'edit_comment', $comment->comment_parent ) ) {
1924 /* translators: Comment moderation. %s: Parent comment edit URL. */
1925 $notify_message .= sprintf( __( 'In reply to: %s' ), admin_url( "comment.php?action=editcomment&c={$comment->comment_parent}#wpbody-content" ) ) . "\r\n";
1926 }
1927
1928 /* translators: %s: Comment text. */
1929 $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
1930 $notify_message .= __( 'You can see all comments on this post here:' ) . "\r\n";
1931 /* translators: Comment notification email subject. 1: Site title, 2: Post title. */
1932 $subject = sprintf( __( '[%1$s] Comment: "%2$s"' ), $blogname, $post->post_title );
1933 break;
1934 }
1935
1936 /* translators: %s: Comment URL. */
1937 if ( 'note' === $comment->comment_type ) {
1938 $notify_message .= get_edit_post_link( $comment->comment_post_ID, 'url' ) . "\r\n";
1939 } else {
1940 $notify_message .= get_permalink( $comment->comment_post_ID ) . "#comments\r\n\r\n";
1941 $notify_message .= sprintf( __( 'Permalink: %s' ), get_comment_link( $comment ) ) . "\r\n";
1942 }
1943
1944 if ( 'note' !== $comment->comment_type && user_can( $post->post_author, 'edit_comment', $comment->comment_ID ) ) {
1945 if ( EMPTY_TRASH_DAYS ) {
1946 /* translators: Comment moderation. %s: Comment action URL. */
1947 $notify_message .= sprintf( __( 'Trash it: %s' ), admin_url( "comment.php?action=trash&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n";
1948 } else {
1949 /* translators: Comment moderation. %s: Comment action URL. */
1950 $notify_message .= sprintf( __( 'Delete it: %s' ), admin_url( "comment.php?action=delete&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n";
1951 }
1952 /* translators: Comment moderation. %s: Comment action URL. */
1953 $notify_message .= sprintf( __( 'Spam it: %s' ), admin_url( "comment.php?action=spam&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n";
1954 }
1955
1956 /**
1957 * Filters the comment notification email text.
1958 *
1959 * @since 1.5.2
1960 *
1961 * @param string $notify_message The comment notification email text.
1962 * @param string $comment_id Comment ID as a numeric string.
1963 */
1964 $notify_message = apply_filters( 'comment_notification_text', $notify_message, $comment->comment_ID );
1965
1966 /**
1967 * Filters the comment notification email subject.
1968 *
1969 * @since 1.5.2
1970 *
1971 * @param string $subject The comment notification email subject.
1972 * @param string $comment_id Comment ID as a numeric string.
1973 */
1974 $subject = apply_filters( 'comment_notification_subject', $subject, $comment->comment_ID );
1975
1976 wp_mail( $email, wp_specialchars_decode( $subject ), $notify_message, $message_headers );
1977
1978 if ( $switched_locale ) {
1979 restore_previous_locale();
1980 }
1981 }
1982
1983 return true;
1984 }
1985endif;
1986
1987if ( ! function_exists( 'wp_notify_moderator' ) ) :
1988 /**
1989 * Notifies the moderator of the site about a new comment that is awaiting approval.
1990 *
1991 * @since 1.0.0
1992 *
1993 * @global wpdb $wpdb WordPress database abstraction object.
1994 *
1995 * Uses the {@see 'notify_moderator'} filter to determine whether the site moderator
1996 * should be notified, overriding the site setting.
1997 *
1998 * @param int $comment_id Comment ID.
1999 * @return true Always returns true.
2000 */
2001 function wp_notify_moderator( $comment_id ) {
2002 global $wpdb;
2003
2004 $maybe_notify = get_option( 'moderation_notify' );
2005
2006 /**
2007 * Filters whether to send the site moderator email notifications, overriding the site setting.
2008 *
2009 * @since 4.4.0
2010 *
2011 * @param bool $maybe_notify Whether to notify blog moderator.
2012 * @param int $comment_id The ID of the comment for the notification.
2013 */
2014 $maybe_notify = apply_filters( 'notify_moderator', $maybe_notify, $comment_id );
2015
2016 if ( ! $maybe_notify ) {
2017 return true;
2018 }
2019
2020 $comment = get_comment( $comment_id );
2021 $post = get_post( $comment->comment_post_ID );
2022 $user = get_userdata( $post->post_author );
2023 // Send to the administration and to the post author if the author can modify the comment.
2024 $emails = array( get_option( 'admin_email' ) );
2025 if ( $user && user_can( $user->ID, 'edit_comment', $comment_id ) && ! empty( $user->user_email ) ) {
2026 if ( 0 !== strcasecmp( $user->user_email, get_option( 'admin_email' ) ) ) {
2027 $emails[] = $user->user_email;
2028 }
2029 }
2030
2031 $comment_author_domain = '';
2032 if ( WP_Http::is_ip_address( $comment->comment_author_IP ) ) {
2033 $comment_author_domain = gethostbyaddr( $comment->comment_author_IP );
2034 }
2035
2036 $comments_waiting = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE comment_approved = '0'" );
2037
2038 /*
2039 * The blogname option is escaped with esc_html() on the way into the database in sanitize_option().
2040 * We want to reverse this for the plain text arena of emails.
2041 */
2042 $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
2043 $comment_content = wp_specialchars_decode( $comment->comment_content );
2044
2045 $message_headers = '';
2046
2047 /**
2048 * Filters the list of recipients for comment moderation emails.
2049 *
2050 * @since 3.7.0
2051 *
2052 * @param string[] $emails List of email addresses to notify for comment moderation.
2053 * @param int $comment_id Comment ID.
2054 */
2055 $emails = apply_filters( 'comment_moderation_recipients', $emails, $comment_id );
2056
2057 /**
2058 * Filters the comment moderation email headers.
2059 *
2060 * @since 2.8.0
2061 *
2062 * @param string $message_headers Headers for the comment moderation email.
2063 * @param int $comment_id Comment ID.
2064 */
2065 $message_headers = apply_filters( 'comment_moderation_headers', $message_headers, $comment_id );
2066
2067 foreach ( $emails as $email ) {
2068 $user = get_user_by( 'email', $email );
2069
2070 if ( $user ) {
2071 $switched_locale = switch_to_user_locale( $user->ID );
2072 } else {
2073 $switched_locale = switch_to_locale( get_locale() );
2074 }
2075
2076 switch ( $comment->comment_type ) {
2077 case 'trackback':
2078 /* translators: %s: Post title. */
2079 $notify_message = sprintf( __( 'A new trackback on the post "%s" is waiting for your approval' ), $post->post_title ) . "\r\n";
2080 $notify_message .= get_permalink( $comment->comment_post_ID ) . "\r\n\r\n";
2081 /* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */
2082 $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
2083 /* translators: %s: Trackback/pingback/comment author URL. */
2084 $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
2085 $notify_message .= __( 'Trackback excerpt: ' ) . "\r\n" . $comment_content . "\r\n\r\n";
2086 break;
2087
2088 case 'pingback':
2089 /* translators: %s: Post title. */
2090 $notify_message = sprintf( __( 'A new pingback on the post "%s" is waiting for your approval' ), $post->post_title ) . "\r\n";
2091 $notify_message .= get_permalink( $comment->comment_post_ID ) . "\r\n\r\n";
2092 /* translators: 1: Trackback/pingback website name, 2: Website IP address, 3: Website hostname. */
2093 $notify_message .= sprintf( __( 'Website: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
2094 /* translators: %s: Trackback/pingback/comment author URL. */
2095 $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
2096 $notify_message .= __( 'Pingback excerpt: ' ) . "\r\n" . $comment_content . "\r\n\r\n";
2097 break;
2098
2099 default: // Comments.
2100 /* translators: %s: Post title. */
2101 $notify_message = sprintf( __( 'A new comment on the post "%s" is waiting for your approval' ), $post->post_title ) . "\r\n";
2102 $notify_message .= get_permalink( $comment->comment_post_ID ) . "\r\n\r\n";
2103 /* translators: 1: Comment author's name, 2: Comment author's IP address, 3: Comment author's hostname. */
2104 $notify_message .= sprintf( __( 'Author: %1$s (IP address: %2$s, %3$s)' ), $comment->comment_author, $comment->comment_author_IP, $comment_author_domain ) . "\r\n";
2105 /* translators: %s: Comment author email. */
2106 $notify_message .= sprintf( __( 'Email: %s' ), $comment->comment_author_email ) . "\r\n";
2107 /* translators: %s: Trackback/pingback/comment author URL. */
2108 $notify_message .= sprintf( __( 'URL: %s' ), $comment->comment_author_url ) . "\r\n";
2109
2110 if ( $comment->comment_parent ) {
2111 /* translators: Comment moderation. %s: Parent comment edit URL. */
2112 $notify_message .= sprintf( __( 'In reply to: %s' ), admin_url( "comment.php?action=editcomment&c={$comment->comment_parent}#wpbody-content" ) ) . "\r\n";
2113 }
2114
2115 /* translators: %s: Comment text. */
2116 $notify_message .= sprintf( __( 'Comment: %s' ), "\r\n" . $comment_content ) . "\r\n\r\n";
2117 break;
2118 }
2119
2120 /* translators: Comment moderation. %s: Comment action URL. */
2121 $notify_message .= sprintf( __( 'Approve it: %s' ), admin_url( "comment.php?action=approve&c={$comment_id}#wpbody-content" ) ) . "\r\n";
2122
2123 if ( EMPTY_TRASH_DAYS ) {
2124 /* translators: Comment moderation. %s: Comment action URL. */
2125 $notify_message .= sprintf( __( 'Trash it: %s' ), admin_url( "comment.php?action=trash&c={$comment_id}#wpbody-content" ) ) . "\r\n";
2126 } else {
2127 /* translators: Comment moderation. %s: Comment action URL. */
2128 $notify_message .= sprintf( __( 'Delete it: %s' ), admin_url( "comment.php?action=delete&c={$comment_id}#wpbody-content" ) ) . "\r\n";
2129 }
2130
2131 /* translators: Comment moderation. %s: Comment action URL. */
2132 $notify_message .= sprintf( __( 'Spam it: %s' ), admin_url( "comment.php?action=spam&c={$comment_id}#wpbody-content" ) ) . "\r\n";
2133
2134 $notify_message .= sprintf(
2135 /* translators: Comment moderation. %s: Number of comments awaiting approval. */
2136 _n(
2137 'Currently %s comment is waiting for approval. Please visit the moderation panel:',
2138 'Currently %s comments are waiting for approval. Please visit the moderation panel:',
2139 $comments_waiting
2140 ),
2141 number_format_i18n( $comments_waiting )
2142 ) . "\r\n";
2143 $notify_message .= admin_url( 'edit-comments.php?comment_status=moderated#wpbody-content' ) . "\r\n";
2144
2145 /* translators: Comment moderation notification email subject. 1: Site title, 2: Post title. */
2146 $subject = sprintf( __( '[%1$s] Please moderate: "%2$s"' ), $blogname, $post->post_title );
2147
2148 /**
2149 * Filters the comment moderation email text.
2150 *
2151 * @since 1.5.2
2152 *
2153 * @param string $notify_message Text of the comment moderation email.
2154 * @param int $comment_id Comment ID.
2155 */
2156 $notify_message = apply_filters( 'comment_moderation_text', $notify_message, $comment_id );
2157
2158 /**
2159 * Filters the comment moderation email subject.
2160 *
2161 * @since 1.5.2
2162 *
2163 * @param string $subject Subject of the comment moderation email.
2164 * @param int $comment_id Comment ID.
2165 */
2166 $subject = apply_filters( 'comment_moderation_subject', $subject, $comment_id );
2167
2168 wp_mail( $email, wp_specialchars_decode( $subject ), $notify_message, $message_headers );
2169
2170 if ( $switched_locale ) {
2171 restore_previous_locale();
2172 }
2173 }
2174
2175 return true;
2176 }
2177endif;
2178
2179if ( ! function_exists( 'wp_password_change_notification' ) ) :
2180 /**
2181 * Notifies the blog admin of a user changing password, normally via email.
2182 *
2183 * @since 2.7.0
2184 *
2185 * @param WP_User $user User object.
2186 */
2187 function wp_password_change_notification( $user ) {
2188 /*
2189 * Send a copy of password change notification to the admin,
2190 * but check to see if it's the admin whose password we're changing, and skip this.
2191 */
2192 if ( 0 !== strcasecmp( $user->user_email, get_option( 'admin_email' ) ) ) {
2193
2194 $admin_user = get_user_by( 'email', get_option( 'admin_email' ) );
2195
2196 if ( $admin_user ) {
2197 $switched_locale = switch_to_user_locale( $admin_user->ID );
2198 } else {
2199 $switched_locale = switch_to_locale( get_locale() );
2200 }
2201
2202 /* translators: %s: User name. */
2203 $message = sprintf( __( 'Password changed for user: %s' ), $user->user_login ) . "\r\n";
2204 /*
2205 * The blogname option is escaped with esc_html() on the way into the database in sanitize_option().
2206 * We want to reverse this for the plain text arena of emails.
2207 */
2208 $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
2209
2210 $wp_password_change_notification_email = array(
2211 'to' => get_option( 'admin_email' ),
2212 /* translators: Password change notification email subject. %s: Site title. */
2213 'subject' => __( '[%s] Password Changed' ),
2214 'message' => $message,
2215 'headers' => '',
2216 );
2217
2218 /**
2219 * Filters the contents of the password change notification email sent to the site admin.
2220 *
2221 * @since 4.9.0
2222 *
2223 * @param array $wp_password_change_notification_email {
2224 * Used to build wp_mail().
2225 *
2226 * @type string $to The intended recipient - site admin email address.
2227 * @type string $subject The subject of the email.
2228 * @type string $message The body of the email.
2229 * @type string $headers The headers of the email.
2230 * }
2231 * @param WP_User $user User object for user whose password was changed.
2232 * @param string $blogname The site title.
2233 */
2234 $wp_password_change_notification_email = apply_filters( 'wp_password_change_notification_email', $wp_password_change_notification_email, $user, $blogname );
2235
2236 wp_mail(
2237 $wp_password_change_notification_email['to'],
2238 wp_specialchars_decode( sprintf( $wp_password_change_notification_email['subject'], $blogname ) ),
2239 $wp_password_change_notification_email['message'],
2240 $wp_password_change_notification_email['headers']
2241 );
2242
2243 if ( $switched_locale ) {
2244 restore_previous_locale();
2245 }
2246 }
2247 }
2248endif;
2249
2250if ( ! function_exists( 'wp_new_user_notification' ) ) :
2251 /**
2252 * Emails login credentials to a newly-registered user.
2253 *
2254 * A new user registration notification is also sent to admin email.
2255 *
2256 * @since 2.0.0
2257 * @since 4.3.0 The `$plaintext_pass` parameter was changed to `$notify`.
2258 * @since 4.3.1 The `$plaintext_pass` parameter was deprecated. `$notify` added as a third parameter.
2259 * @since 4.6.0 The `$notify` parameter accepts 'user' for sending notification only to the user created.
2260 *
2261 * @param int $user_id User ID.
2262 * @param null $deprecated Not used (argument deprecated).
2263 * @param string $notify Optional. Type of notification that should happen. Accepts 'admin' or an empty
2264 * string (admin only), 'user', or 'both' (admin and user). Default empty.
2265 */
2266 function wp_new_user_notification( $user_id, $deprecated = null, $notify = '' ) {
2267 if ( null !== $deprecated ) {
2268 _deprecated_argument( __FUNCTION__, '4.3.1' );
2269 }
2270
2271 // Accepts only 'user', 'admin' , 'both' or default '' as $notify.
2272 if ( ! in_array( $notify, array( 'user', 'admin', 'both', '' ), true ) ) {
2273 return;
2274 }
2275
2276 $user = get_userdata( $user_id );
2277
2278 /*
2279 * The blogname option is escaped with esc_html() on the way into the database in sanitize_option().
2280 * We want to reverse this for the plain text arena of emails.
2281 */
2282 $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
2283
2284 /**
2285 * Filters whether the admin is notified of a new user registration.
2286 *
2287 * @since 6.1.0
2288 *
2289 * @param bool $send Whether to send the email. Default true.
2290 * @param WP_User $user User object for new user.
2291 */
2292 $send_notification_to_admin = apply_filters( 'wp_send_new_user_notification_to_admin', true, $user );
2293
2294 if ( 'user' !== $notify && true === $send_notification_to_admin ) {
2295
2296 $admin_user = get_user_by( 'email', get_option( 'admin_email' ) );
2297
2298 if ( $admin_user ) {
2299 $switched_locale = switch_to_user_locale( $admin_user->ID );
2300 } else {
2301 $switched_locale = switch_to_locale( get_locale() );
2302 }
2303
2304 /* translators: %s: Site title. */
2305 $message = sprintf( __( 'New user registration on your site %s:' ), $blogname ) . "\r\n\r\n";
2306 /* translators: %s: User login. */
2307 $message .= sprintf( __( 'Username: %s' ), $user->user_login ) . "\r\n\r\n";
2308 /* translators: %s: User email address. */
2309 $message .= sprintf( __( 'Email: %s' ), $user->user_email ) . "\r\n";
2310
2311 $wp_new_user_notification_email_admin = array(
2312 'to' => get_option( 'admin_email' ),
2313 /* translators: New user registration notification email subject. %s: Site title. */
2314 'subject' => __( '[%s] New User Registration' ),
2315 'message' => $message,
2316 'headers' => '',
2317 );
2318
2319 /**
2320 * Filters the contents of the new user notification email sent to the site admin.
2321 *
2322 * @since 4.9.0
2323 *
2324 * @param array $wp_new_user_notification_email_admin {
2325 * Used to build wp_mail().
2326 *
2327 * @type string $to The intended recipient - site admin email address.
2328 * @type string $subject The subject of the email.
2329 * @type string $message The body of the email.
2330 * @type string $headers The headers of the email.
2331 * }
2332 * @param WP_User $user User object for new user.
2333 * @param string $blogname The site title.
2334 */
2335 $wp_new_user_notification_email_admin = apply_filters( 'wp_new_user_notification_email_admin', $wp_new_user_notification_email_admin, $user, $blogname );
2336
2337 wp_mail(
2338 $wp_new_user_notification_email_admin['to'],
2339 wp_specialchars_decode( sprintf( $wp_new_user_notification_email_admin['subject'], $blogname ) ),
2340 $wp_new_user_notification_email_admin['message'],
2341 $wp_new_user_notification_email_admin['headers']
2342 );
2343
2344 if ( $switched_locale ) {
2345 restore_previous_locale();
2346 }
2347 }
2348
2349 /**
2350 * Filters whether the user is notified of their new user registration.
2351 *
2352 * @since 6.1.0
2353 *
2354 * @param bool $send Whether to send the email. Default true.
2355 * @param WP_User $user User object for new user.
2356 */
2357 $send_notification_to_user = apply_filters( 'wp_send_new_user_notification_to_user', true, $user );
2358
2359 // `$deprecated` was pre-4.3 `$plaintext_pass`. An empty `$plaintext_pass` didn't sent a user notification.
2360 if ( 'admin' === $notify || true !== $send_notification_to_user || ( empty( $deprecated ) && empty( $notify ) ) ) {
2361 return;
2362 }
2363
2364 $key = get_password_reset_key( $user );
2365 if ( is_wp_error( $key ) ) {
2366 return;
2367 }
2368
2369 $switched_locale = switch_to_user_locale( $user_id );
2370
2371 /* translators: %s: User login. */
2372 $message = sprintf( __( 'Username: %s' ), $user->user_login ) . "\r\n\r\n";
2373 $message .= __( 'To set your password, visit the following address:' ) . "\r\n\r\n";
2374
2375 /*
2376 * Since some user login names end in a period, this could produce ambiguous URLs that
2377 * end in a period. To avoid the ambiguity, ensure that the login is not the last query
2378 * arg in the URL. If moving it to the end, a trailing period will need to be escaped.
2379 *
2380 * @see https://core.trac.wordpress.org/tickets/42957
2381 */
2382 $message .= network_site_url( 'wp-login.php?login=' . rawurlencode( $user->user_login ) . "&key=$key&action=rp", 'login' ) . "\r\n\r\n";
2383
2384 $message .= wp_login_url() . "\r\n";
2385
2386 $wp_new_user_notification_email = array(
2387 'to' => $user->user_email,
2388 /* translators: Login details notification email subject. %s: Site title. */
2389 'subject' => __( '[%s] Login Details' ),
2390 'message' => $message,
2391 'headers' => '',
2392 );
2393
2394 /**
2395 * Filters the contents of the new user notification email sent to the new user.
2396 *
2397 * @since 4.9.0
2398 *
2399 * @param array $wp_new_user_notification_email {
2400 * Used to build wp_mail().
2401 *
2402 * @type string $to The intended recipient - New user email address.
2403 * @type string $subject The subject of the email.
2404 * @type string $message The body of the email.
2405 * @type string $headers The headers of the email.
2406 * }
2407 * @param WP_User $user User object for new user.
2408 * @param string $blogname The site title.
2409 */
2410 $wp_new_user_notification_email = apply_filters( 'wp_new_user_notification_email', $wp_new_user_notification_email, $user, $blogname );
2411
2412 wp_mail(
2413 $wp_new_user_notification_email['to'],
2414 wp_specialchars_decode( sprintf( $wp_new_user_notification_email['subject'], $blogname ) ),
2415 $wp_new_user_notification_email['message'],
2416 $wp_new_user_notification_email['headers']
2417 );
2418
2419 if ( $switched_locale ) {
2420 restore_previous_locale();
2421 }
2422 }
2423endif;
2424
2425if ( ! function_exists( 'wp_nonce_tick' ) ) :
2426 /**
2427 * Returns the time-dependent variable for nonce creation.
2428 *
2429 * A nonce has a lifespan of two ticks. Nonces in their second tick may be
2430 * updated, e.g. by autosave.
2431 *
2432 * @since 2.5.0
2433 * @since 6.1.0 Added `$action` argument.
2434 *
2435 * @param string|int $action Optional. The nonce action. Default -1.
2436 * @return float Float value rounded up to the next highest integer.
2437 */
2438 function wp_nonce_tick( $action = -1 ) {
2439 /**
2440 * Filters the lifespan of nonces in seconds.
2441 *
2442 * @since 2.5.0
2443 * @since 6.1.0 Added `$action` argument to allow for more targeted filters.
2444 *
2445 * @param int $lifespan Lifespan of nonces in seconds. Default 86,400 seconds, or one day.
2446 * @param string|int $action The nonce action, or -1 if none was provided.
2447 */
2448 $nonce_life = apply_filters( 'nonce_life', DAY_IN_SECONDS, $action );
2449
2450 return ceil( time() / ( $nonce_life / 2 ) );
2451 }
2452endif;
2453
2454if ( ! function_exists( 'wp_verify_nonce' ) ) :
2455 /**
2456 * Verifies that a correct security nonce was used with time limit.
2457 *
2458 * A nonce is valid for between 12 and 24 hours (by default).
2459 *
2460 * @since 2.0.3
2461 *
2462 * @param string $nonce Nonce value that was used for verification, usually via a form field.
2463 * @param string|int $action Should give context to what is taking place and be the same when nonce was created.
2464 * @return int|false 1 if the nonce is valid and generated between 0-12 hours ago,
2465 * 2 if the nonce is valid and generated between 12-24 hours ago.
2466 * False if the nonce is invalid.
2467 */
2468 function wp_verify_nonce( $nonce, $action = -1 ) {
2469 $nonce = (string) $nonce;
2470 $user = wp_get_current_user();
2471 $uid = (int) $user->ID;
2472 if ( ! $uid ) {
2473 /**
2474 * Filters whether the user who generated the nonce is logged out.
2475 *
2476 * @since 3.5.0
2477 *
2478 * @param int $uid ID of the nonce-owning user.
2479 * @param string|int $action The nonce action, or -1 if none was provided.
2480 */
2481 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
2482 }
2483
2484 if ( empty( $nonce ) ) {
2485 return false;
2486 }
2487
2488 $token = wp_get_session_token();
2489 $i = wp_nonce_tick( $action );
2490
2491 // Nonce generated 0-12 hours ago.
2492 $expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
2493 if ( hash_equals( $expected, $nonce ) ) {
2494 return 1;
2495 }
2496
2497 // Nonce generated 12-24 hours ago.
2498 $expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
2499 if ( hash_equals( $expected, $nonce ) ) {
2500 return 2;
2501 }
2502
2503 /**
2504 * Fires when nonce verification fails.
2505 *
2506 * @since 4.4.0
2507 *
2508 * @param string $nonce The invalid nonce.
2509 * @param string|int $action The nonce action.
2510 * @param WP_User $user The current user object.
2511 * @param string $token The user's session token.
2512 */
2513 do_action( 'wp_verify_nonce_failed', $nonce, $action, $user, $token );
2514
2515 // Invalid nonce.
2516 return false;
2517 }
2518endif;
2519
2520if ( ! function_exists( 'wp_create_nonce' ) ) :
2521 /**
2522 * Creates a cryptographic token tied to a specific action, user, user session,
2523 * and window of time.
2524 *
2525 * @since 2.0.3
2526 * @since 4.0.0 Session tokens were integrated with nonce creation.
2527 *
2528 * @param string|int $action Scalar value to add context to the nonce.
2529 * @return string The token.
2530 */
2531 function wp_create_nonce( $action = -1 ) {
2532 $user = wp_get_current_user();
2533 $uid = (int) $user->ID;
2534 if ( ! $uid ) {
2535 /** This filter is documented in wp-includes/pluggable.php */
2536 $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
2537 }
2538
2539 $token = wp_get_session_token();
2540 $i = wp_nonce_tick( $action );
2541
2542 return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
2543 }
2544endif;
2545
2546if ( ! function_exists( 'wp_salt' ) ) :
2547 /**
2548 * Returns a salt to add to hashes.
2549 *
2550 * Salts are created using secret keys. Secret keys are located in two places:
2551 * in the database and in the wp-config.php file. The secret key in the database
2552 * is randomly generated and will be appended to the secret keys in wp-config.php.
2553 *
2554 * The secret keys in wp-config.php should be updated to strong, random keys to maximize
2555 * security. Below is an example of how the secret key constants are defined.
2556 * Do not paste this example directly into wp-config.php. Instead, have a
2557 * {@link https://api.wordpress.org/secret-key/1.1/salt/ secret key created} just
2558 * for you.
2559 *
2560 * define('AUTH_KEY', ' Xakm<o xQy rw4EMsLKM-?!T+,PFF})H4lzcW57AF0U@N@< >M%G4Yt>f`z]MON');
2561 * define('SECURE_AUTH_KEY', 'LzJ}op]mr|6+![P}Ak:uNdJCJZd>(Hx.-Mh#Tz)pCIU#uGEnfFz|f ;;eU%/U^O~');
2562 * define('LOGGED_IN_KEY', '|i|Ux`9<p-h$aFf(qnT:sDO:D1P^wZ$$/Ra@miTJi9G;ddp_<q}6H1)o|a +&JCM');
2563 * define('NONCE_KEY', '%:R{[P|,s.KuMltH5}cI;/k<Gx~j!f0I)m_sIyu+&NJZ)-iO>z7X>QYR0Z_XnZ@|');
2564 * define('AUTH_SALT', 'eZyT)-Naw]F8CwA*VaW#q*|.)g@o}||wf~@C-YSt}(dh_r6EbI#A,y|nU2{B#JBW');
2565 * define('SECURE_AUTH_SALT', '!=oLUTXh,QW=H `}`L|9/^4-3 STz},T(w}W<I`.JjPi)<Bmf1v,HpGe}T1:Xt7n');
2566 * define('LOGGED_IN_SALT', '+XSqHc;@Q*K_b|Z?NC[3H!!EONbh.n<+=uKR:>*c(u`g~EJBf#8u#R{mUEZrozmm');
2567 * define('NONCE_SALT', 'h`GXHhD>SLWVfg1(1(N{;.V!MoE(SfbA_ksP@&`+AycHcAV$+?@3q+rxV{%^VyKT');
2568 *
2569 * Salting passwords helps against tools which has stored hashed values of
2570 * common dictionary strings. The added values makes it harder to crack.
2571 *
2572 * @since 2.5.0
2573 *
2574 * @link https://api.wordpress.org/secret-key/1.1/salt/ Create secrets for wp-config.php
2575 *
2576 * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce).
2577 * @return string Salt value
2578 */
2579 function wp_salt( $scheme = 'auth' ) {
2580 static $cached_salts = array();
2581 if ( isset( $cached_salts[ $scheme ] ) ) {
2582 /**
2583 * Filters the WordPress salt.
2584 *
2585 * @since 2.5.0
2586 *
2587 * @param string $cached_salt Cached salt for the given scheme.
2588 * @param string $scheme Authentication scheme. Values include 'auth',
2589 * 'secure_auth', 'logged_in', and 'nonce'.
2590 */
2591 return apply_filters( 'salt', $cached_salts[ $scheme ], $scheme );
2592 }
2593
2594 static $duplicated_keys;
2595 if ( null === $duplicated_keys ) {
2596 $duplicated_keys = array();
2597
2598 foreach ( array( 'AUTH', 'SECURE_AUTH', 'LOGGED_IN', 'NONCE', 'SECRET' ) as $first ) {
2599 foreach ( array( 'KEY', 'SALT' ) as $second ) {
2600 if ( ! defined( "{$first}_{$second}" ) ) {
2601 continue;
2602 }
2603 $value = constant( "{$first}_{$second}" );
2604 $duplicated_keys[ $value ] = isset( $duplicated_keys[ $value ] );
2605 }
2606 }
2607
2608 $duplicated_keys['put your unique phrase here'] = true;
2609
2610 /*
2611 * translators: This string should only be translated if wp-config-sample.php is localized.
2612 * You can check the localized release package or
2613 * https://i18n.svn.wordpress.org/<locale code>/branches/<wp version>/dist/wp-config-sample.php
2614 */
2615 $duplicated_keys[ __( 'put your unique phrase here' ) ] = true;
2616 }
2617
2618 /*
2619 * Determine which options to prime.
2620 *
2621 * If the salt keys are undefined, use a duplicate value or the
2622 * default `put your unique phrase here` value the salt will be
2623 * generated via `wp_generate_password()` and stored as a site
2624 * option. These options will be primed to avoid repeated
2625 * database requests for undefined salts.
2626 */
2627 $options_to_prime = array();
2628 foreach ( array( 'auth', 'secure_auth', 'logged_in', 'nonce' ) as $key ) {
2629 foreach ( array( 'key', 'salt' ) as $second ) {
2630 $const = strtoupper( "{$key}_{$second}" );
2631 if ( ! defined( $const ) || true === $duplicated_keys[ constant( $const ) ] ) {
2632 $options_to_prime[] = "{$key}_{$second}";
2633 }
2634 }
2635 }
2636
2637 if ( ! empty( $options_to_prime ) ) {
2638 /*
2639 * Also prime `secret_key` used for undefined salting schemes.
2640 *
2641 * If the scheme is unknown, the default value for `secret_key` will be
2642 * used too for the salt. This should rarely happen, so the option is only
2643 * primed if other salts are undefined.
2644 *
2645 * At this point of execution it is known that a database call will be made
2646 * to prime salts, so the `secret_key` option can be primed regardless of the
2647 * constants status.
2648 */
2649 $options_to_prime[] = 'secret_key';
2650 wp_prime_site_option_caches( $options_to_prime );
2651 }
2652
2653 $values = array(
2654 'key' => '',
2655 'salt' => '',
2656 );
2657 if ( defined( 'SECRET_KEY' ) && SECRET_KEY && empty( $duplicated_keys[ SECRET_KEY ] ) ) {
2658 $values['key'] = SECRET_KEY;
2659 }
2660 if ( 'auth' === $scheme && defined( 'SECRET_SALT' ) && SECRET_SALT && empty( $duplicated_keys[ SECRET_SALT ] ) ) {
2661 $values['salt'] = SECRET_SALT;
2662 }
2663
2664 if ( in_array( $scheme, array( 'auth', 'secure_auth', 'logged_in', 'nonce' ), true ) ) {
2665 foreach ( array( 'key', 'salt' ) as $type ) {
2666 $const = strtoupper( "{$scheme}_{$type}" );
2667 if ( defined( $const ) && constant( $const ) && empty( $duplicated_keys[ constant( $const ) ] ) ) {
2668 $values[ $type ] = constant( $const );
2669 } elseif ( ! $values[ $type ] ) {
2670 $values[ $type ] = get_site_option( "{$scheme}_{$type}" );
2671 if ( ! $values[ $type ] ) {
2672 $values[ $type ] = wp_generate_password( 64, true, true );
2673 update_site_option( "{$scheme}_{$type}", $values[ $type ] );
2674 }
2675 }
2676 }
2677 } else {
2678 if ( ! $values['key'] ) {
2679 $values['key'] = get_site_option( 'secret_key' );
2680 if ( ! $values['key'] ) {
2681 $values['key'] = wp_generate_password( 64, true, true );
2682 update_site_option( 'secret_key', $values['key'] );
2683 }
2684 }
2685 $values['salt'] = hash_hmac( 'md5', $scheme, $values['key'] );
2686 }
2687
2688 $cached_salts[ $scheme ] = $values['key'] . $values['salt'];
2689
2690 /** This filter is documented in wp-includes/pluggable.php */
2691 return apply_filters( 'salt', $cached_salts[ $scheme ], $scheme );
2692 }
2693endif;
2694
2695if ( ! function_exists( 'wp_hash' ) ) :
2696 /**
2697 * Gets the hash of the given string.
2698 *
2699 * The default algorithm is md5 but can be changed to any algorithm supported by
2700 * `hash_hmac()`. Use the `hash_hmac_algos()` function to check the supported
2701 * algorithms.
2702 *
2703 * @since 2.0.3
2704 * @since 6.8.0 The `$algo` parameter was added.
2705 *
2706 * @throws InvalidArgumentException if the hashing algorithm is not supported.
2707 *
2708 * @param string $data Plain text to hash.
2709 * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce).
2710 * @param string $algo Hashing algorithm to use. Default: 'md5'.
2711 * @return string Hash of $data.
2712 */
2713 function wp_hash( $data, $scheme = 'auth', $algo = 'md5' ) {
2714 $salt = wp_salt( $scheme );
2715
2716 // Ensure the algorithm is supported by the hash_hmac function.
2717 if ( ! in_array( $algo, hash_hmac_algos(), true ) ) {
2718 throw new InvalidArgumentException(
2719 sprintf(
2720 /* translators: 1: Name of a cryptographic hash algorithm. 2: List of supported algorithms. */
2721 __( 'Unsupported hashing algorithm: %1$s. Supported algorithms are: %2$s' ),
2722 $algo,
2723 implode( ', ', hash_hmac_algos() )
2724 )
2725 );
2726 }
2727
2728 return hash_hmac( $algo, $data, $salt );
2729 }
2730endif;
2731
2732if ( ! function_exists( 'wp_hash_password' ) ) :
2733 /**
2734 * Creates a hash of a plain text password.
2735 *
2736 * For integration with other applications, this function can be overwritten to
2737 * instead use the other package password hashing algorithm.
2738 *
2739 * @since 2.5.0
2740 * @since 6.8.0 The password is now hashed using bcrypt by default instead of phpass.
2741 *
2742 * @global PasswordHash $wp_hasher phpass object.
2743 *
2744 * @param string $password Plain text user password to hash.
2745 * @return string The hash string of the password.
2746 */
2747 function wp_hash_password(
2748 #[\SensitiveParameter]
2749 $password
2750 ) {
2751 global $wp_hasher;
2752
2753 if ( ! empty( $wp_hasher ) ) {
2754 return $wp_hasher->HashPassword( trim( $password ) );
2755 }
2756
2757 if ( strlen( $password ) > 4096 ) {
2758 return '*';
2759 }
2760
2761 /**
2762 * Filters the hashing algorithm to use in the password_hash() and password_needs_rehash() functions.
2763 *
2764 * The default is the value of the `PASSWORD_BCRYPT` constant which means bcrypt is used.
2765 *
2766 * **Important:** The only password hashing algorithm that is guaranteed to be available across PHP
2767 * installations is bcrypt. If you use any other algorithm you must make sure that it is available on
2768 * the server. The `password_algos()` function can be used to check which hashing algorithms are available.
2769 *
2770 * The hashing options can be controlled via the {@see 'wp_hash_password_options'} filter.
2771 *
2772 * Other available constants include:
2773 *
2774 * - `PASSWORD_ARGON2I`
2775 * - `PASSWORD_ARGON2ID`
2776 * - `PASSWORD_DEFAULT`
2777 *
2778 * The values of the algorithm constants are strings in PHP 7.4+ and integers in PHP 7.3 and earlier.
2779 *
2780 * @since 6.8.0
2781 *
2782 * @param string|int $algorithm The hashing algorithm. Default is the value of the `PASSWORD_BCRYPT` constant.
2783 */
2784 $algorithm = apply_filters( 'wp_hash_password_algorithm', PASSWORD_BCRYPT );
2785
2786 /**
2787 * Filters the options passed to the password_hash() and password_needs_rehash() functions.
2788 *
2789 * The default hashing algorithm is bcrypt, but this can be changed via the {@see 'wp_hash_password_algorithm'}
2790 * filter. You must ensure that the options are appropriate for the algorithm in use.
2791 *
2792 * The values of the algorithm constants are strings in PHP 7.4+ and integers in PHP 7.3 and earlier.
2793 *
2794 * @since 6.8.0
2795 *
2796 * @param array $options Array of options to pass to the password hashing functions.
2797 * By default this is an empty array which means the default
2798 * options will be used.
2799 * @param string|int $algorithm The hashing algorithm in use.
2800 */
2801 $options = apply_filters( 'wp_hash_password_options', array(), $algorithm );
2802
2803 // Algorithms other than bcrypt don't need to use pre-hashing.
2804 if ( PASSWORD_BCRYPT !== $algorithm ) {
2805 return password_hash( $password, $algorithm, $options );
2806 }
2807
2808 // Use SHA-384 to retain entropy from a password that's longer than 72 bytes, and a `wp-sha384` key for domain separation.
2809 $password_to_hash = base64_encode( hash_hmac( 'sha384', trim( $password ), 'wp-sha384', true ) );
2810
2811 // Add a prefix to facilitate distinguishing vanilla bcrypt hashes.
2812 return '$wp' . password_hash( $password_to_hash, $algorithm, $options );
2813 }
2814endif;
2815
2816if ( ! function_exists( 'wp_check_password' ) ) :
2817 /**
2818 * Checks a plaintext password against a hashed password.
2819 *
2820 * Note that this function may be used to check a value that is not a user password.
2821 * A plugin may use this function to check a password of a different type, and there
2822 * may not always be a user ID associated with the password.
2823 *
2824 * For integration with other applications, this function can be overwritten to
2825 * instead use the other package password hashing algorithm.
2826 *
2827 * @since 2.5.0
2828 * @since 6.8.0 Passwords in WordPress are now hashed with bcrypt by default. A
2829 * password that wasn't hashed with bcrypt will be checked with phpass.
2830 *
2831 * @global PasswordHash $wp_hasher phpass object. Used as a fallback for verifying
2832 * passwords that were hashed with phpass.
2833 *
2834 * @param string $password Plaintext password.
2835 * @param string $hash Hash of the password to check against.
2836 * @param string|int $user_id Optional. ID of a user associated with the password.
2837 * @return bool False, if the $password does not match the hashed password.
2838 */
2839 function wp_check_password(
2840 #[\SensitiveParameter]
2841 $password,
2842 $hash,
2843 $user_id = ''
2844 ) {
2845 global $wp_hasher;
2846
2847 if ( strlen( $hash ) <= 32 ) {
2848 // Check the hash using md5 regardless of the current hashing mechanism.
2849 $check = hash_equals( $hash, md5( $password ) );
2850 } elseif ( ! empty( $wp_hasher ) ) {
2851 // Check the password using the overridden hasher.
2852 $check = $wp_hasher->CheckPassword( $password, $hash );
2853 } elseif ( strlen( $password ) > 4096 ) {
2854 // Passwords longer than 4096 characters are not supported.
2855 $check = false;
2856 } elseif ( str_starts_with( $hash, '$wp' ) ) {
2857 // Check the password using the current prefixed hash.
2858 $password_to_verify = base64_encode( hash_hmac( 'sha384', $password, 'wp-sha384', true ) );
2859 $check = password_verify( $password_to_verify, substr( $hash, 3 ) );
2860 } elseif ( str_starts_with( $hash, '$P$' ) ) {
2861 // Check the password using phpass.
2862 require_once ABSPATH . WPINC . '/class-phpass.php';
2863 $check = ( new PasswordHash( 8, true ) )->CheckPassword( $password, $hash );
2864 } else {
2865 // Check the password using compat support for any non-prefixed hash.
2866 $check = password_verify( $password, $hash );
2867 }
2868
2869 /**
2870 * Filters whether the plaintext password matches the hashed password.
2871 *
2872 * @since 2.5.0
2873 * @since 6.8.0 Passwords are now hashed with bcrypt by default.
2874 * Old passwords may still be hashed with phpass or md5.
2875 *
2876 * @param bool $check Whether the passwords match.
2877 * @param string $password The plaintext password.
2878 * @param string $hash The hashed password.
2879 * @param string|int $user_id Optional ID of a user associated with the password.
2880 * Can be empty.
2881 */
2882 return apply_filters( 'check_password', $check, $password, $hash, $user_id );
2883 }
2884endif;
2885
2886if ( ! function_exists( 'wp_password_needs_rehash' ) ) :
2887 /**
2888 * Checks whether a password hash needs to be rehashed.
2889 *
2890 * Passwords are hashed with bcrypt using the default cost. A password hashed in a prior version
2891 * of WordPress may still be hashed with phpass and will need to be rehashed. If the default cost
2892 * or algorithm is changed in PHP or WordPress then a password hashed in a previous version will
2893 * need to be rehashed.
2894 *
2895 * Note that, just like wp_check_password(), this function may be used to check a value that is
2896 * not a user password. A plugin may use this function to check a password of a different type,
2897 * and there may not always be a user ID associated with the password.
2898 *
2899 * @since 6.8.0
2900 *
2901 * @global PasswordHash $wp_hasher phpass object.
2902 *
2903 * @param string $hash Hash of a password to check.
2904 * @param string|int $user_id Optional. ID of a user associated with the password.
2905 * @return bool Whether the hash needs to be rehashed.
2906 */
2907 function wp_password_needs_rehash( $hash, $user_id = '' ) {
2908 global $wp_hasher;
2909
2910 if ( ! empty( $wp_hasher ) ) {
2911 return false;
2912 }
2913
2914 /** This filter is documented in wp-includes/pluggable.php */
2915 $algorithm = apply_filters( 'wp_hash_password_algorithm', PASSWORD_BCRYPT );
2916
2917 /** This filter is documented in wp-includes/pluggable.php */
2918 $options = apply_filters( 'wp_hash_password_options', array(), $algorithm );
2919
2920 $prefixed = str_starts_with( $hash, '$wp' );
2921
2922 if ( ( PASSWORD_BCRYPT === $algorithm ) && ! $prefixed ) {
2923 // If bcrypt is in use and the hash is not prefixed then it needs to be rehashed.
2924 $needs_rehash = true;
2925 } else {
2926 // Otherwise check the hash minus its prefix if necessary.
2927 $hash_to_check = $prefixed ? substr( $hash, 3 ) : $hash;
2928 $needs_rehash = password_needs_rehash( $hash_to_check, $algorithm, $options );
2929 }
2930
2931 /**
2932 * Filters whether the password hash needs to be rehashed.
2933 *
2934 * @since 6.8.0
2935 *
2936 * @param bool $needs_rehash Whether the password hash needs to be rehashed.
2937 * @param string $hash The password hash.
2938 * @param string|int $user_id Optional. ID of a user associated with the password.
2939 */
2940 return apply_filters( 'password_needs_rehash', $needs_rehash, $hash, $user_id );
2941 }
2942endif;
2943
2944if ( ! function_exists( 'wp_generate_password' ) ) :
2945 /**
2946 * Generates a random password drawn from the defined set of characters.
2947 *
2948 * Uses wp_rand() to create passwords with far less predictability
2949 * than similar native PHP functions like `rand()` or `mt_rand()`.
2950 *
2951 * @since 2.5.0
2952 *
2953 * @param int $length Optional. The length of password to generate. Default 12.
2954 * @param bool $special_chars Optional. Whether to include standard special characters.
2955 * Default true.
2956 * @param bool $extra_special_chars Optional. Whether to include other special characters.
2957 * Used when generating secret keys and salts. Default false.
2958 * @return string The random password.
2959 */
2960 function wp_generate_password( $length = 12, $special_chars = true, $extra_special_chars = false ) {
2961 $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
2962 if ( $special_chars ) {
2963 $chars .= '!@#$%^&*()';
2964 }
2965 if ( $extra_special_chars ) {
2966 $chars .= '-_ []{}<>~`+=,.;:/?|';
2967 }
2968
2969 $password = '';
2970 for ( $i = 0; $i < $length; $i++ ) {
2971 $password .= substr( $chars, wp_rand( 0, strlen( $chars ) - 1 ), 1 );
2972 }
2973
2974 /**
2975 * Filters the randomly-generated password.
2976 *
2977 * @since 3.0.0
2978 * @since 5.3.0 Added the `$length`, `$special_chars`, and `$extra_special_chars` parameters.
2979 *
2980 * @param string $password The generated password.
2981 * @param int $length The length of password to generate.
2982 * @param bool $special_chars Whether to include standard special characters.
2983 * @param bool $extra_special_chars Whether to include other special characters.
2984 */
2985 return apply_filters( 'random_password', $password, $length, $special_chars, $extra_special_chars );
2986 }
2987endif;
2988
2989if ( ! function_exists( 'wp_rand' ) ) :
2990 /**
2991 * Generates a random non-negative number.
2992 *
2993 * @since 2.6.2
2994 * @since 4.4.0 Uses PHP7 random_int() or the random_compat library if available.
2995 * @since 6.1.0 Returns zero instead of a random number if both `$min` and `$max` are zero.
2996 *
2997 * @global string $rnd_value
2998 *
2999 * @param int $min Optional. Lower limit for the generated number.
3000 * Accepts positive integers or zero. Defaults to 0.
3001 * @param int $max Optional. Upper limit for the generated number.
3002 * Accepts positive integers. Defaults to 4294967295.
3003 * @return int A random non-negative number between min and max.
3004 */
3005 function wp_rand( $min = null, $max = null ) {
3006 global $rnd_value;
3007
3008 /*
3009 * Some misconfigured 32-bit environments (Entropy PHP, for example)
3010 * truncate integers larger than PHP_INT_MAX to PHP_INT_MAX rather than overflowing them to floats.
3011 */
3012 $max_random_number = 3000000000 === 2147483647 ? (float) '4294967295' : 4294967295; // 4294967295 = 0xffffffff
3013
3014 if ( null === $min ) {
3015 $min = 0;
3016 }
3017
3018 if ( null === $max ) {
3019 $max = $max_random_number;
3020 }
3021
3022 // We only handle ints, floats are truncated to their integer value.
3023 $min = (int) $min;
3024 $max = (int) $max;
3025
3026 // Use PHP's CSPRNG, or a compatible method.
3027 static $use_random_int_functionality = true;
3028 if ( $use_random_int_functionality ) {
3029 try {
3030 // wp_rand() can accept arguments in either order, PHP cannot.
3031 $_max = max( $min, $max );
3032 $_min = min( $min, $max );
3033 $val = random_int( $_min, $_max );
3034 if ( false !== $val ) {
3035 return absint( $val );
3036 } else {
3037 $use_random_int_functionality = false;
3038 }
3039 } catch ( Error $e ) {
3040 $use_random_int_functionality = false;
3041 } catch ( Exception $e ) {
3042 $use_random_int_functionality = false;
3043 }
3044 }
3045
3046 /*
3047 * Reset $rnd_value after 14 uses.
3048 * 32 (md5) + 40 (sha1) + 40 (sha1) / 8 = 14 random numbers from $rnd_value.
3049 */
3050 if ( strlen( $rnd_value ) < 8 ) {
3051 if ( defined( 'WP_SETUP_CONFIG' ) ) {
3052 static $seed = '';
3053 } else {
3054 $seed = get_transient( 'random_seed' );
3055 }
3056 $rnd_value = md5( uniqid( microtime() . mt_rand(), true ) . $seed );
3057 $rnd_value .= sha1( $rnd_value );
3058 $rnd_value .= sha1( $rnd_value . $seed );
3059 $seed = md5( $seed . $rnd_value );
3060 if ( ! defined( 'WP_SETUP_CONFIG' ) && ! defined( 'WP_INSTALLING' ) ) {
3061 set_transient( 'random_seed', $seed );
3062 }
3063 }
3064
3065 // Take the first 8 digits for our value.
3066 $value = substr( $rnd_value, 0, 8 );
3067
3068 // Strip the first eight, leaving the remainder for the next call to wp_rand().
3069 $rnd_value = substr( $rnd_value, 8 );
3070
3071 $value = abs( hexdec( $value ) );
3072
3073 // Reduce the value to be within the min - max range.
3074 $value = $min + ( $max - $min + 1 ) * $value / ( $max_random_number + 1 );
3075
3076 return abs( (int) $value );
3077 }
3078endif;
3079
3080if ( ! function_exists( 'wp_set_password' ) ) :
3081 /**
3082 * Updates the user's password with a new hashed one.
3083 *
3084 * For integration with other applications, this function can be overwritten to
3085 * instead use the other package password checking algorithm.
3086 *
3087 * Please note: This function should be used sparingly and is really only meant for single-time
3088 * application. Leveraging this improperly in a plugin or theme could result in an endless loop
3089 * of password resets if precautions are not taken to ensure it does not execute on every page load.
3090 *
3091 * @since 2.5.0
3092 * @since 6.8.0 The password is now hashed using bcrypt by default instead of phpass.
3093 *
3094 * @global wpdb $wpdb WordPress database abstraction object.
3095 *
3096 * @param string $password The plaintext new user password.
3097 * @param int $user_id User ID.
3098 */
3099 function wp_set_password(
3100 #[\SensitiveParameter]
3101 $password,
3102 $user_id
3103 ) {
3104 global $wpdb;
3105
3106 $old_user_data = get_userdata( $user_id );
3107
3108 $hash = wp_hash_password( $password );
3109 $wpdb->update(
3110 $wpdb->users,
3111 array(
3112 'user_pass' => $hash,
3113 'user_activation_key' => '',
3114 ),
3115 array( 'ID' => $user_id )
3116 );
3117
3118 clean_user_cache( $user_id );
3119
3120 /**
3121 * Fires after the user password is set.
3122 *
3123 * @since 6.2.0
3124 * @since 6.7.0 The `$old_user_data` parameter was added.
3125 *
3126 * @param string $password The plaintext password just set.
3127 * @param int $user_id The ID of the user whose password was just set.
3128 * @param WP_User $old_user_data Object containing user's data prior to update.
3129 */
3130 do_action( 'wp_set_password', $password, $user_id, $old_user_data );
3131 }
3132endif;
3133
3134if ( ! function_exists( 'get_avatar' ) ) :
3135 /**
3136 * Retrieves the avatar `<img>` tag for a user, email address, MD5 hash, comment, or post.
3137 *
3138 * @since 2.5.0
3139 * @since 4.2.0 Added the optional `$args` parameter.
3140 * @since 5.5.0 Added the `loading` argument.
3141 * @since 6.1.0 Added the `decoding` argument.
3142 * @since 6.3.0 Added the `fetchpriority` argument.
3143 *
3144 * @param mixed $id_or_email The avatar to retrieve. Accepts a user ID, Gravatar MD5 hash,
3145 * user email, WP_User object, WP_Post object, or WP_Comment object.
3146 * @param int $size Optional. Height and width of the avatar in pixels. Default 96.
3147 * @param string $default_value URL for the default image or a default type. Accepts:
3148 * - '404' (return a 404 instead of a default image)
3149 * - 'retro' (a 8-bit arcade-style pixelated face)
3150 * - 'robohash' (a robot)
3151 * - 'monsterid' (a monster)
3152 * - 'wavatar' (a cartoon face)
3153 * - 'identicon' (the "quilt", a geometric pattern)
3154 * - 'initials' (initials based avatar with background color)
3155 * - 'color' (generated background color)
3156 * - 'mystery', 'mm', or 'mysteryman' (The Oyster Man)
3157 * - 'blank' (transparent GIF)
3158 * - 'gravatar_default' (the Gravatar logo)
3159 * Default is the value of the 'avatar_default' option,
3160 * with a fallback of 'mystery'.
3161 * @param string $alt Optional. Alternative text to use in the avatar image tag.
3162 * Default empty.
3163 * @param array $args {
3164 * Optional. Extra arguments to retrieve the avatar.
3165 *
3166 * @type int $height Display height of the avatar in pixels. Defaults to $size.
3167 * @type int $width Display width of the avatar in pixels. Defaults to $size.
3168 * @type bool $force_default Whether to always show the default image, never the Gravatar.
3169 * Default false.
3170 * @type string $rating What rating to display avatars up to. Accepts:
3171 * - 'G' (suitable for all audiences)
3172 * - 'PG' (possibly offensive, usually for audiences 13 and above)
3173 * - 'R' (intended for adult audiences above 17)
3174 * - 'X' (even more mature than above)
3175 * Default is the value of the 'avatar_rating' option.
3176 * @type string $scheme URL scheme to use. See set_url_scheme() for accepted values.
3177 * Default null.
3178 * @type array|string $class Array or string of additional classes to add to the img element.
3179 * Default null.
3180 * @type bool $force_display Whether to always show the avatar - ignores the show_avatars option.
3181 * Default false.
3182 * @type string $loading Value for the `loading` attribute.
3183 * Default null.
3184 * @type string $fetchpriority Value for the `fetchpriority` attribute.
3185 * Default null.
3186 * @type string $decoding Value for the `decoding` attribute.
3187 * Default null.
3188 * @type string $extra_attr HTML attributes to insert in the IMG element. Is not sanitized.
3189 * Default empty.
3190 * }
3191 * @return string|false `<img>` tag for the user's avatar. False on failure.
3192 */
3193 function get_avatar( $id_or_email, $size = 96, $default_value = '', $alt = '', $args = null ) {
3194 $defaults = array(
3195 // get_avatar_data() args.
3196 'size' => 96,
3197 'height' => null,
3198 'width' => null,
3199 'default' => get_option( 'avatar_default', 'mystery' ),
3200 'force_default' => false,
3201 'rating' => get_option( 'avatar_rating' ),
3202 'scheme' => null,
3203 'alt' => '',
3204 'class' => null,
3205 'force_display' => false,
3206 'loading' => null,
3207 'fetchpriority' => null,
3208 'decoding' => null,
3209 'extra_attr' => '',
3210 );
3211
3212 if ( empty( $args ) ) {
3213 $args = array();
3214 }
3215
3216 $args['size'] = (int) $size;
3217 $args['default'] = $default_value;
3218 $args['alt'] = $alt;
3219
3220 $args = wp_parse_args( $args, $defaults );
3221
3222 if ( empty( $args['height'] ) ) {
3223 $args['height'] = $args['size'];
3224 }
3225 if ( empty( $args['width'] ) ) {
3226 $args['width'] = $args['size'];
3227 }
3228
3229 // Update args with loading optimized attributes.
3230 $loading_optimization_attr = wp_get_loading_optimization_attributes( 'img', $args, 'get_avatar' );
3231
3232 $args = array_merge( $args, $loading_optimization_attr );
3233
3234 if ( is_object( $id_or_email ) && isset( $id_or_email->comment_ID ) ) {
3235 $id_or_email = get_comment( $id_or_email );
3236 }
3237
3238 /**
3239 * Allows the HTML for a user's avatar to be returned early.
3240 *
3241 * Returning a non-null value will effectively short-circuit get_avatar(), passing
3242 * the value through the {@see 'get_avatar'} filter and returning early.
3243 *
3244 * @since 4.2.0
3245 *
3246 * @param string|null $avatar HTML for the user's avatar. Default null.
3247 * @param mixed $id_or_email The avatar to retrieve. Accepts a user ID, Gravatar MD5 hash,
3248 * user email, WP_User object, WP_Post object, or WP_Comment object.
3249 * @param array $args Arguments passed to get_avatar_url(), after processing.
3250 */
3251 $avatar = apply_filters( 'pre_get_avatar', null, $id_or_email, $args );
3252
3253 if ( ! is_null( $avatar ) ) {
3254 /** This filter is documented in wp-includes/pluggable.php */
3255 return apply_filters( 'get_avatar', $avatar, $id_or_email, $args['size'], $args['default'], $args['alt'], $args );
3256 }
3257
3258 if ( ! $args['force_display'] && ! get_option( 'show_avatars' ) ) {
3259 return false;
3260 }
3261
3262 $url2x = get_avatar_url( $id_or_email, array_merge( $args, array( 'size' => $args['size'] * 2 ) ) );
3263
3264 $args = get_avatar_data( $id_or_email, $args );
3265
3266 $url = $args['url'];
3267
3268 if ( ! $url || is_wp_error( $url ) ) {
3269 return false;
3270 }
3271
3272 $class = array( 'avatar', 'avatar-' . (int) $args['size'], 'photo' );
3273
3274 if ( ! $args['found_avatar'] || $args['force_default'] ) {
3275 $class[] = 'avatar-default';
3276 }
3277
3278 if ( $args['class'] ) {
3279 if ( is_array( $args['class'] ) ) {
3280 $class = array_merge( $class, $args['class'] );
3281 } else {
3282 $class[] = $args['class'];
3283 }
3284 }
3285
3286 // Add `loading`, `fetchpriority`, and `decoding` attributes.
3287 $extra_attr = $args['extra_attr'];
3288
3289 if ( in_array( $args['loading'], array( 'lazy', 'eager' ), true )
3290 && ! preg_match( '/\bloading\s*=/', $extra_attr )
3291 ) {
3292 if ( ! empty( $extra_attr ) ) {
3293 $extra_attr .= ' ';
3294 }
3295
3296 $extra_attr .= "loading='{$args['loading']}'";
3297 }
3298
3299 if ( in_array( $args['fetchpriority'], array( 'high', 'low', 'auto' ), true )
3300 && ! preg_match( '/\bfetchpriority\s*=/', $extra_attr )
3301 ) {
3302 if ( ! empty( $extra_attr ) ) {
3303 $extra_attr .= ' ';
3304 }
3305
3306 $extra_attr .= "fetchpriority='{$args['fetchpriority']}'";
3307 }
3308
3309 if ( in_array( $args['decoding'], array( 'async', 'sync', 'auto' ), true )
3310 && ! preg_match( '/\bdecoding\s*=/', $extra_attr )
3311 ) {
3312 if ( ! empty( $extra_attr ) ) {
3313 $extra_attr .= ' ';
3314 }
3315
3316 $extra_attr .= "decoding='{$args['decoding']}'";
3317 }
3318
3319 $avatar = sprintf(
3320 "<img alt='%s' src='%s' srcset='%s' class='%s' height='%d' width='%d' %s/>",
3321 esc_attr( $args['alt'] ),
3322 esc_url( $url ),
3323 esc_url( $url2x ) . ' 2x',
3324 esc_attr( implode( ' ', $class ) ),
3325 (int) $args['height'],
3326 (int) $args['width'],
3327 $extra_attr
3328 );
3329
3330 /**
3331 * Filters the HTML for a user's avatar.
3332 *
3333 * @since 2.5.0
3334 * @since 4.2.0 Added the `$args` parameter.
3335 *
3336 * @param string $avatar HTML for the user's avatar.
3337 * @param mixed $id_or_email The avatar to retrieve. Accepts a user ID, Gravatar MD5 hash,
3338 * user email, WP_User object, WP_Post object, or WP_Comment object.
3339 * @param int $size Height and width of the avatar in pixels.
3340 * @param string $default_value URL for the default image or a default type. Accepts:
3341 * - '404' (return a 404 instead of a default image)
3342 * - 'retro' (a 8-bit arcade-style pixelated face)
3343 * - 'robohash' (a robot)
3344 * - 'monsterid' (a monster)
3345 * - 'wavatar' (a cartoon face)
3346 * - 'identicon' (the "quilt", a geometric pattern)
3347 * - 'mystery', 'mm', or 'mysteryman' (The Oyster Man)
3348 * - 'blank' (transparent GIF)
3349 * - 'gravatar_default' (the Gravatar logo)
3350 * @param string $alt Alternative text to use in the avatar image tag.
3351 * @param array $args Arguments passed to get_avatar_data(), after processing.
3352 */
3353 return apply_filters( 'get_avatar', $avatar, $id_or_email, $args['size'], $args['default'], $args['alt'], $args );
3354 }
3355endif;
3356
3357if ( ! function_exists( 'wp_text_diff' ) ) :
3358 /**
3359 * Displays a human readable HTML representation of the difference between two strings.
3360 *
3361 * The Diff is available for getting the changes between versions. The output is
3362 * HTML, so the primary use is for displaying the changes. If the two strings
3363 * are equivalent, then an empty string will be returned.
3364 *
3365 * @since 2.6.0
3366 *
3367 * @see wp_parse_args() Used to change defaults to user defined settings.
3368 * @uses Text_Diff
3369 * @uses WP_Text_Diff_Renderer_Table
3370 *
3371 * @param string $left_string "old" (left) version of string.
3372 * @param string $right_string "new" (right) version of string.
3373 * @param string|array $args {
3374 * Associative array of options to pass to WP_Text_Diff_Renderer_Table().
3375 *
3376 * @type string $title Titles the diff in a manner compatible
3377 * with the output. Default empty.
3378 * @type string $title_left Change the HTML to the left of the title.
3379 * Default empty.
3380 * @type string $title_right Change the HTML to the right of the title.
3381 * Default empty.
3382 * @type bool $show_split_view True for split view (two columns), false for
3383 * un-split view (single column). Default true.
3384 * }
3385 * @return string Empty string if strings are equivalent or HTML with differences.
3386 */
3387 function wp_text_diff( $left_string, $right_string, $args = null ) {
3388 $defaults = array(
3389 'title' => '',
3390 'title_left' => '',
3391 'title_right' => '',
3392 'show_split_view' => true,
3393 );
3394 $args = wp_parse_args( $args, $defaults );
3395
3396 if ( ! class_exists( 'WP_Text_Diff_Renderer_Table', false ) ) {
3397 require ABSPATH . WPINC . '/wp-diff.php';
3398 }
3399
3400 $left_string = normalize_whitespace( $left_string );
3401 $right_string = normalize_whitespace( $right_string );
3402
3403 $left_lines = explode( "\n", $left_string );
3404 $right_lines = explode( "\n", $right_string );
3405 $text_diff = new Text_Diff( $left_lines, $right_lines );
3406 $renderer = new WP_Text_Diff_Renderer_Table( $args );
3407 $diff = $renderer->render( $text_diff );
3408
3409 if ( ! $diff ) {
3410 return '';
3411 }
3412
3413 $is_split_view = ! empty( $args['show_split_view'] );
3414 $is_split_view_class = $is_split_view ? ' is-split-view' : '';
3415
3416 $r = "<table class='diff$is_split_view_class'>\n";
3417
3418 if ( $args['title'] ) {
3419 $r .= "<caption class='diff-title'>$args[title]</caption>\n";
3420 }
3421
3422 if ( $args['title_left'] || $args['title_right'] ) {
3423 $r .= '<thead>';
3424 }
3425
3426 if ( $args['title_left'] || $args['title_right'] ) {
3427 $th_or_td_left = empty( $args['title_left'] ) ? 'td' : 'th';
3428 $th_or_td_right = empty( $args['title_right'] ) ? 'td' : 'th';
3429
3430 $r .= "<tr class='diff-sub-title'>\n";
3431 $r .= "\t<$th_or_td_left>$args[title_left]</$th_or_td_left>\n";
3432 if ( $is_split_view ) {
3433 $r .= "\t<$th_or_td_right>$args[title_right]</$th_or_td_right>\n";
3434 }
3435 $r .= "</tr>\n";
3436 }
3437
3438 if ( $args['title_left'] || $args['title_right'] ) {
3439 $r .= "</thead>\n";
3440 }
3441
3442 $r .= "<tbody>\n$diff\n</tbody>\n";
3443 $r .= '</table>';
3444
3445 return $r;
3446 }
3447endif;
3448
Ui Ux Design – Teachers Night Out https://cardgames4educators.com Wed, 16 Oct 2024 22:24:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.4 https://cardgames4educators.com/wp-content/uploads/2024/06/cropped-Card-4-Educators-logo-32x32.png Ui Ux Design – Teachers Night Out https://cardgames4educators.com 32 32 Masters In English How English Speaker https://cardgames4educators.com/masters-in-english-how-english-speaker/ https://cardgames4educators.com/masters-in-english-how-english-speaker/#comments Mon, 27 May 2024 08:54:45 +0000 https://themexriver.com/wp/kadu/?p=1

Erat himenaeos neque id sagittis massa. Hac suscipit pulvinar dignissim platea magnis eu. Don tellus a pharetra inceptos efficitur dui pulvinar. Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent pulvinar odio volutpat parturient. Quisque risus finibus suspendisse mus purus magnis facilisi condimentum consectetur dui. Curae elit suspendisse cursus vehicula.

Turpis taciti class non vel pretium quis pulvinar tempor lobortis nunc. Libero phasellus parturient sapien volutpat malesuada ornare. Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae. Porta est tempor ex eget feugiat vulputate ipsum. Justo nec iaculis habitant diam arcu fermentum.

We offer comprehen sive emplo ment services such as assistance wit employer compliance.Our company is your strategic HR partner as instead of HR. john smithson

Cubilia dignissim sollicitudin rhoncus lacinia maximus. Cras lorem fermentum bibendum pellentesque nisl etiam ligula enim cubilia. Vulputate pede sapien torquent montes tempus malesuada in mattis dis turpis vitae.

Exploring Learning Landscapes in Academic

Feugiat facilisis penatibus pulvinar nunc dictumst donec odio platea habitasse. Lacus porta dolor purus elit ante bibendum tortor netus taciti nullam cubilia. Erat per suspendisse placerat morbi egestas pulvinar bibendum sollicitudin nec. Euismod cubilia eleifend velit himenaeos sodales lectus. Leo maximus cras ac porttitor aliquam torquent.

]]>
https://cardgames4educators.com/masters-in-english-how-english-speaker/feed/ 1