PluginProbe
DoLogin Security / trunk
DoLogin Security vtrunk
5.0.10 4.8.3 trunk 1.0 1.1 1.1.1 1.2 1.2.1 1.2.2 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 1.6 All 64 releases
dologin / src / util.cls.php

util.cls.php in DoLogin Security trunk, at src/util.cls.php

360 lines 9.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Utility class
4 *
5 * @since 1.1
6 */
7 namespace dologin;
8
9 defined( 'WPINC' ) || exit;
10
11 class Util extends Instance {
12 /**
13 * Init Utility
14 *
15 * @since 1.1
16 * @access public
17 */
18 public function init() {
19 }
20
21 /**
22 * Builds an url with an action and a nonce.
23 *
24 * @since 1.4
25 * @access public
26 */
27 public static function build_url( $action, $type = false, $is_ajax = false, $page = null, $append_arr = null ) {
28 $prefix = '?';
29
30 if ( ! $is_ajax ) {
31 if ( $page ) {
32 // If use admin url
33 if ( $page === true ) {
34 $page = 'admin.php';
35 } elseif ( strpos( $page, '?' ) !== false ) {
36 $prefix = '&';
37 }
38 $combined = $page . $prefix . Router::ACTION . '=' . $action;
39 } else {
40 // Current page rebuild URL
41 $params = $_GET; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- rebuilding the current admin page URL only, no state change.
42
43 if ( ! empty( $params ) ) {
44 if ( isset( $params['DOLOGIN_ACTION'] ) ) {
45 unset( $params['DOLOGIN_ACTION'] );
46 }
47 if ( isset( $params['_wpnonce'] ) ) {
48 unset( $params['_wpnonce'] );
49 }
50 if ( ! empty( $params ) ) {
51 $prefix .= http_build_query( $params ) . '&';
52 }
53 }
54 global $pagenow;
55 $combined = $pagenow . $prefix . Router::ACTION . '=' . $action;
56 }
57 } else {
58 $combined = 'admin-ajax.php?action=dologin_ajax&' . Router::ACTION . '=' . $action;
59 }
60
61 if ( is_network_admin() ) {
62 $prenonce = network_admin_url( $combined );
63 } else {
64 $prenonce = admin_url( $combined );
65 }
66 $url = wp_nonce_url( $prenonce, $action, Router::NONCE );
67
68 if ( $type ) {
69 // Remove potential param `type` from url
70 $url = wp_parse_url( htmlspecialchars_decode( $url ) );
71 parse_str( $url['query'], $query );
72
73 $built_arr = array_merge( $query, array( Router::TYPE => $type ) );
74 if ( $append_arr ) {
75 $built_arr = array_merge( $built_arr, $append_arr );
76 }
77 $url['query'] = http_build_query( $built_arr );
78 self::compatibility();
79 $url = http_build_url( $url );
80 }
81
82 return $url;
83 }
84
85 /**
86 * Improve compatibility to PHP old versions
87 *
88 * @since 1.2.2
89 */
90 public static function compatibility() {
91 require_once DOLOGIN_DIR . 'lib/php-compatibility.func.php';
92 }
93
94 /**
95 * Check if is login page or not
96 *
97 * @since 1.3
98 * @access public
99 */
100 public static function is_login_page() {
101 $is_login_page = in_array( $GLOBALS['pagenow'], array( 'wp-login.php', 'wp-register.php' ), true );
102
103 return apply_filters( 'dologin_is_login_page', $is_login_page );
104 }
105
106 /**
107 * Version check
108 *
109 * @since 1.1
110 * @access public
111 */
112 public static function version_check( $tag ) {
113 return false;
114 }
115
116 /**
117 * Set seconds/timestamp to readable format
118 *
119 * @since 1.2
120 * @access public
121 */
122 public static function readable_time( $seconds_or_timestamp, $timeout = 3600, $backward = true ) {
123 if ( strlen( $seconds_or_timestamp ) == 10 ) {
124 $seconds = time() - $seconds_or_timestamp;
125 if ( $seconds > $timeout ) {
126 return date_i18n( 'm/d/Y H:i:s', $seconds_or_timestamp + get_option( 'gmt_offset' ) * 60 * 60 );
127 }
128 } else {
129 $seconds = $seconds_or_timestamp;
130 }
131 $res = '';
132 if ( $seconds > 86400 ) {
133 $num = floor( $seconds / 86400 );
134 $res .= $num . 'd';
135 $seconds %= 86400;
136 }
137 if ( $seconds > 3600 ) {
138 if ( $res ) {
139 $res .= ', ';
140 }
141 $num = floor( $seconds / 3600 );
142 $res .= $num . 'h';
143 $seconds %= 3600;
144 }
145 if ( $seconds > 60 ) {
146 if ( $res ) {
147 $res .= ', ';
148 }
149 $num = floor( $seconds / 60 );
150 $res .= $num . 'm';
151 $seconds %= 60;
152 }
153 if ( $seconds > 0 ) {
154 if ( $res ) {
155 $res .= ' ';
156 }
157 $res .= $seconds . 's';
158 }
159 if ( ! $res ) {
160 return $backward ? __( 'just now', 'dologin' ) : __( 'right now', 'dologin' );
161 }
162 /* translators: %s: human-readable elapsed time such as "5m" or "2h". */
163 $res = $backward ? sprintf( __( ' %s ago', 'dologin' ), $res ) : $res;
164 return $res;
165 }
166
167 /**
168 * Generate pagination
169 *
170 * @since 2.7
171 * @access public
172 */
173 public static function pagination( $total, $limit, $return_offset = false ) {
174 $pagenum = isset( $_GET['pagenum'] ) ? max( 1, absint( $_GET['pagenum'] ) ) : 1; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- reading pagination index only, no state change.
175 $total = max( 0, absint( $total ) );
176 $limit = max( 1, absint( $limit ) );
177 $num_of_pages = (int) ceil( $total / $limit );
178 $pagenum = min( $pagenum, max( 1, $num_of_pages ) );
179 $offset = ( $pagenum - 1 ) * $limit;
180
181 if ( $return_offset ) {
182 return $offset;
183 }
184
185 $page_links = paginate_links(
186 array(
187 'base' => add_query_arg( 'pagenum', '%#%' ),
188 'format' => '',
189 'prev_text' => __( '&laquo;', 'dologin' ),
190 'next_text' => __( '&raquo;', 'dologin' ),
191 'total' => $num_of_pages,
192 'current' => $pagenum,
193 )
194 );
195
196 return '<div class="tablenav"><div class="tablenav-pages" style="margin: 1em 0">' . $page_links . '</div></div>';
197 }
198
199 /**
200 * Deactivate
201 *
202 * @since 1.1
203 * @access public
204 */
205 public static function deactivate() {
206 delete_transient( 'dologin_activation_redirect' );
207
208 self::version_check( 'deactivate' );
209 }
210
211 /**
212 * Uninstall clearance
213 *
214 * @since 1.1
215 * @access public
216 */
217 public static function uninstall() {
218 self::version_check( 'uninstall' );
219
220 if ( is_multisite() ) {
221 self::run_for_sites( 'delete_data' );
222 } else {
223 self::delete_site_data();
224 }
225 self::delete_user_data();
226 }
227
228 /**
229 * Delete plugin tables and options from the current site.
230 */
231 private static function delete_site_data() {
232 global $wpdb;
233
234 Data::cls()->tables_del();
235 $q = "SELECT option_name FROM `$wpdb->options` WHERE option_name LIKE %s OR option_name LIKE %s OR option_name LIKE %s OR option_name LIKE %s";
236 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery -- $wpdb->options is the current site's internal options table; prefixes are prepared and delete_option() clears caches.
237 $option_names = $wpdb->get_col(
238 $wpdb->prepare(
239 $q,
240 $wpdb->esc_like( 'dologin.' ) . '%',
241 $wpdb->esc_like( 'dologin_kl_' ) . '%',
242 $wpdb->esc_like( '_transient_dologin_' ) . '%',
243 $wpdb->esc_like( '_transient_timeout_dologin_' ) . '%'
244 )
245 );
246 foreach ( (array) $option_names as $option_name ) {
247 delete_option( (string) $option_name );
248 }
249 }
250
251 /**
252 * Delete plugin user metadata shared by the WordPress installation.
253 */
254 private static function delete_user_data() {
255 global $wpdb;
256
257 $q = "SELECT DISTINCT meta_key FROM `$wpdb->usermeta` WHERE meta_key = %s OR meta_key LIKE %s";
258 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery -- $wpdb->usermeta is the internal usermeta table; keys are prepared and delete_metadata() clears caches.
259 $meta_keys = $wpdb->get_col(
260 $wpdb->prepare(
261 $q,
262 '2fa',
263 $wpdb->esc_like( 'dologin_kl_' ) . '%'
264 )
265 );
266 foreach ( (array) $meta_keys as $meta_key ) {
267 delete_metadata( 'user', 0, (string) $meta_key, '', true );
268 }
269 }
270
271 /**
272 * Activation redirect
273 *
274 * @since 1.2.2
275 * @access public
276 */
277 public static function activate( $network_wide = false ) {
278 if ( ! defined( 'SILENCE_INSTALL' ) ) {
279 set_transient( 'dologin_activation_redirect', true, 30 );
280 }
281
282 if ( is_multisite() && $network_wide ) {
283 self::run_for_sites( 'create_tables' );
284 return;
285 }
286 Data::cls()->tables_create();
287 }
288
289 /**
290 * Provision plugin tables for a newly created site during network activation.
291 *
292 * @since 4.6.5
293 */
294 public static function new_site( $site ) {
295 if ( ! is_multisite() || ! self::is_network_active() ) {
296 return;
297 }
298 $site_id = is_object( $site ) && isset( $site->blog_id ) ? (int) $site->blog_id : (int) $site;
299 if ( $site_id > 0 ) {
300 self::run_for_site( $site_id, 'create_tables' );
301 }
302 }
303
304 /**
305 * Run plugin data lifecycle operations on every site in a multisite network.
306 */
307 private static function run_for_sites( $operation ) {
308 $site_ids = array();
309 if ( function_exists( 'get_sites' ) ) {
310 $site_ids = get_sites(
311 array(
312 'fields' => 'ids',
313 'number' => 0,
314 )
315 );
316 } elseif ( function_exists( 'wp_get_sites' ) ) {
317 $legacy_sites = wp_get_sites( array( 'limit' => 0 ) );
318 foreach ( $legacy_sites as $legacy_site ) {
319 if ( isset( $legacy_site['blog_id'] ) ) {
320 $site_ids[] = (int) $legacy_site['blog_id'];
321 }
322 }
323 }
324
325 foreach ( $site_ids as $site_id ) {
326 self::run_for_site( (int) $site_id, $operation );
327 }
328 }
329
330 /**
331 * Safely switch site context and run a plugin data lifecycle operation.
332 */
333 private static function run_for_site( $site_id, $operation ) {
334 $switched = is_multisite() && (int) get_current_blog_id() !== (int) $site_id;
335 if ( $switched ) {
336 switch_to_blog( $site_id );
337 }
338 if ( 'delete_data' === $operation ) {
339 self::delete_site_data();
340 } elseif ( 'delete_tables' === $operation ) {
341 Data::cls()->tables_del();
342 } else {
343 Data::cls()->tables_create();
344 }
345 if ( $switched ) {
346 restore_current_blog();
347 }
348 }
349
350 /**
351 * Check network activation without assuming admin helper functions are loaded.
352 */
353 private static function is_network_active() {
354 if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
355 require_once ABSPATH . 'wp-admin/includes/plugin.php';
356 }
357 return is_plugin_active_for_network( plugin_basename( DOLOGIN_DIR . 'dologin.php' ) );
358 }
359 }
360