* @license GPL-3.0
* @link https://iplocationblock.com/
* @copyright 2021 darkog
* @copyright 2013-2019 tokkonopapa
*/
class IP_Location_Block_Util {
/**
* Returns the storage dir, if it doesn't exist creates it. Works with recursive option.
*
* @param null $target
*
* @return string
*/
public static function get_storage_dir( $target = null ) {
require_once IP_LOCATION_BLOCK_PATH . 'classes/class-ip-location-block-file.php';
$file_system = IP_Location_Block_FS::init( __FUNCTION__ );
$wp_upload_dir = wp_upload_dir();
// Root dir
$dir_path = IP_Location_Block_Util::slashit( IP_Location_Block_Util::slashit( $wp_upload_dir['basedir'] ) . 'ip-location-block' );
if ( ! $file_system->exists( $dir_path ) ) {
$file_system->mkdir( $dir_path );
}
// Init sub_dir path
$sub_dir = $dir_path;
// Create sub dirs
if ( ! empty( $target ) ) {
$sub_dirs = explode( DIRECTORY_SEPARATOR, $target );
foreach ( $sub_dirs as $dir ) {
$sub_dir = IP_Location_Block_Util::slashit( $sub_dir ) . $dir;
if ( ! $file_system->exists( $sub_dir ) ) {
$file_system->mkdir( $sub_dir );
}
}
}
return IP_Location_Block_Util::slashit( $sub_dir );
}
/**
*
* @param null $dir
*
* @return string
*/
public static function get_databases_storage_dir( $dir = null ) {
return self::get_storage_dir( 'databases' . DIRECTORY_SEPARATOR . $dir );
}
/**
* Return the drop ins storage dir
*
* @param $dropin_name
*
* @return string
*/
public static function get_dropins_storage_dir( $dropin_name ) {
return self::get_storage_dir( 'dropins' ) . $dropin_name;
}
/**
* Return local time of day.
*
* @param bool $timestamp
* @param null $fmt
*
* @return string
*/
public static function localdate( $timestamp = false, $fmt = null ) {
static $offset = null;
static $format = null;
null === $offset and $offset = wp_timezone_override_offset() * HOUR_IN_SECONDS; // @since 0.2.8.0
null === $format and $format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
return date_i18n( $fmt ? $fmt : $format, $timestamp ? (int) $timestamp + $offset : false );
}
/**
* Download zip/gz file, un-compress and save it to specified file
*
* @param $url
* @param $args
* @param $files
* @param $modified
*
* @return array|bool
*/
public static function download_zip( $url, $args, $files, $modified ) {
$subdir = 'ilb-' . substr( base_convert( md5( $url ), 16, 32 ), 0, 12 );
$tmp = self::get_temp_dir( $subdir );
if ( is_wp_error( $tmp ) ) {
throw new Exception(
sprintf( __( 'Unable to extract archive. %s', 'ip-location-block' ), $tmp->get_error_message() )
);
}
require_once IP_LOCATION_BLOCK_PATH . 'classes/class-ip-location-block-file.php';
$fs = IP_Location_Block_FS::init( __FUNCTION__ );
// get extension
$ext = strtolower( pathinfo( $url, PATHINFO_EXTENSION ) );
if ( 'tar.gz' === substr( $url, - 6 ) ) {
$ext = 'tar';
}
// check file (1st parameter includes absolute path in case of array)
$filename = is_array( $files ) ? $files[0] : (string) $files;
if ( ! $fs->exists( $filename ) ) {
$modified = 0;
}
// set 'If-Modified-Since' request header
$args += array(
'headers' => array(
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Encoding' => 'gzip, deflate',
'If-Modified-Since' => gmdate( DATE_RFC1123, (int) $modified ),
),
);
// fetch file and get response code & message
if ( isset( $args['method'] ) && 'GET' === $args['method'] ) {
$src = wp_remote_get( ( $url = esc_url_raw( $url ) ), $args );
} else {
$src = wp_remote_head( ( $url = esc_url_raw( $url ) ), $args );
}
if ( is_wp_error( $src ) ) {
return array(
'code' => $src->get_error_code(),
'message' => $src->get_error_message(),
);
}
$code = wp_remote_retrieve_response_code( $src );
$mesg = wp_remote_retrieve_response_message( $src );
$data = wp_remote_retrieve_header( $src, 'last-modified' );
$modified = $data ? strtotime( $data ) : $modified;
if ( 304 == $code ) {
return array(
'code' => $code,
'message' => __( 'Your database file is up-to-date.', 'ip-location-block' ),
'filename' => $filename,
'modified' => $modified,
);
} elseif ( 200 != $code ) {
return array(
'code' => $code,
'message' => $code . ' ' . $mesg,
);
}
try {
// in case that the server which does not support HEAD method
if ( isset( $args['method'] ) && 'GET' === $args['method'] ) {
$data = wp_remote_retrieve_body( $src );
if ( 'gz' === $ext ) {
if ( function_exists( 'gzdecode' ) ) { // @since PHP 5.4.0
if ( false === $fs->put_contents( $filename, gzdecode( $data ) ) ) {
throw new Exception(
sprintf( __( 'Unable to write %s. Please check the permission.', 'ip-location-block' ), $filename )
);
}
} else {
$src = $tmp . basename( $url ); // $src should be removed
$fs->put_contents( $src, $data );
if ( true !== ( $ret = self::gzfile( $src, $filename ) ) ) {
$err = $ret;
}
}
} elseif ( 'tar' === $ext && class_exists( 'PharData', false ) ) { // @since PECL phar 2.0.0
$name = wp_remote_retrieve_header( $src, 'content-disposition' );
$name = explode( 'filename=', $name );
$name = array_pop( $name ); // e.g. GeoLite2-Country_20180102.tar.gz
$src = $tmp . $name; // $src should be removed
// CVE-2015-6833: A directory traversal when extracting ZIP files could be used to overwrite files
// outside of intended area via a `..` in a ZIP archive entry that is mishandled by extractTo().
if ( $fs->put_contents( $src, $data ) ) {
$data = new PharData( $src, FilesystemIterator::SKIP_DOTS ); // get archives
// make the list of contents to be extracted from archives.
// when the list doesn't match the contents in archives, extractTo() may be crushed on windows.
$dst = $data->getSubPathname(); // e.g. GeoLite2-Country_20180102
foreach ( $files as $key => $val ) {
$files[ $key ] = $dst . '/' . basename( $val );
}
// extract specific files from archives into temporary directory and copy it to the destination.
$fpath = self::slashit( $tmp . $dst );
$data->extractTo( $fpath, $files /* NULL */, true );
// copy extracted files to Geolocation APIs directory
$dst = dirname( $filename );
foreach ( $files as $val ) {
// should the destination be exclusive with LOCK_EX ?
// $fs->put_contents( $dst.'/'.basename( $val ), $fs->get_contents( $tmp.'/'.$val ) );
$fs->copy( $fpath . $val, $dst . '/' . basename( $val ), true );
}
}
}
} // downloaded and unzip
else {
// download file
$src = download_url( $url );
if ( is_wp_error( $src ) ) {
throw new Exception(
$src->get_error_code() . ' ' . $src->get_error_message()
);
}
// unzip file
if ( 'gz' === $ext ) {
if ( true !== ( $ret = self::gzfile( $src, $filename ) ) ) {
$err = $ret;
}
} elseif ( 'zip' === $ext && class_exists( 'ZipArchive', false ) ) {
$ret = $fs->unzip_file( $src, $tmp ); // @since 0.2.5
if ( is_wp_error( $ret ) ) {
throw new Exception(
$ret->get_error_code() . ' ' . $ret->get_error_message()
);
}
$f_path = $tmp . basename( $filename );
$data = $fs->get_contents( $f_path );
if ( false === $data ) {
throw new Exception(
sprintf( __( 'Unable to read %s. Please check the permission.', 'ip-location-block' ), $f_path )
);
}
if ( false === $fs->put_contents( $filename, $data ) ) {
throw new Exception(
sprintf( __( 'Unable to write %s. Please check the permission.', 'ip-location-block' ), $filename )
);
}
} else {
throw new Exception( __( 'gz or zip is not supported on your system.', 'ip-location-block' ) );
}
}
} catch ( Exception $e ) {
$err = array(
'code' => $e->getCode(),
'message' => $e->getMessage(),
);
}
if ( ! empty( $gz ) ) {
gzclose( $gz );
}
if ( ! empty( $tmp ) && $fs->exists( $tmp ) ) {
$fs->delete( $tmp, true );
}
return empty( $err ) ? array(
'code' => $code,
'message' => sprintf( __( 'Last update: %s', 'ip-location-block' ), IP_Location_Block_Util::localdate( $modified ) ),
'filename' => $filename,
'modified' => $modified,
) : $err;
}
/**
* Return temporary directory
*
* @param null $subdir
*
* @return string|WP_Error|null
*/
public static function get_temp_dir( $subdir = null ) {
$ds = DIRECTORY_SEPARATOR;
$dir = \get_temp_dir();
$dir = apply_filters( 'ip-location-block-temp-dir', $dir, $subdir );
if ( ! file_exists( $dir ) || ! is_writable( $dir ) ) {
$uploads = wp_upload_dir();
$tmpdir = self::slashit( $uploads['basedir'] ) . 'ip-location-block' . $ds . 'tmp' . $ds;
} else {
$tmpdir = self::slashit( $dir );
}
if ( null !== $subdir ) {
$tmpdir .= ltrim( rtrim( str_replace( '/', $ds, $subdir ), $ds ), $ds ) . $ds;
}
if ( ! file_exists( $tmpdir ) ) {
if ( ! wp_mkdir_p( $tmpdir ) ) {
return new WP_Error( 403, sprintf( 'Temporary directory %s not writable.', $tmpdir ) );
}
}
return $tmpdir;
}
/**
* Decompresses gz archive and output to the file.
*
* @param string $src full path to the downloaded file.
* @param string $dst full path to extracted file.
*
* @return array|bool
*/
private static function gzfile( $src, $dst ) {
try {
if ( false === ( $gz = gzopen( $src, 'r' ) ) ) {
throw new Exception(
sprintf( __( 'Unable to read %s. Please check the permission.', 'ip-location-block' ), $src )
);
}
if ( false === ( $fp = @fopen( $dst, 'cb' ) ) ) {
throw new Exception(
sprintf( __( 'Unable to write %s. Please check the permission.', 'ip-location-block' ), $dst )
);
}
if ( ! flock( $fp, LOCK_EX ) ) {
throw new Exception(
sprintf( __( 'Can\'t lock %s. Please try again after a while.', 'ip-location-block' ), $dst )
);
}
ftruncate( $fp, 0 ); // truncate file
// same block size in wp-includes/class-http.php
while ( $data = gzread( $gz, 4096 ) ) {
fwrite( $fp, $data, strlen( $data ) );
}
} catch ( Exception $e ) {
$err = array(
'code' => $e->getCode(),
'message' => $e->getMessage(),
);
}
if ( ! empty( $fp ) ) {
fflush( $fp ); // flush output before releasing the lock
flock( $fp, LOCK_UN ); // release the lock
fclose( $fp );
}
return empty( $err ) ? true : $err;
}
/**
* Simple comparison of urls
*
* @param $a
* @param $b
*
* @return bool
*/
public static function compare_url( $a, $b ) {
$method = IP_Location_Block_Util::get_request_method();
if ( 'GET' !== $method && 'HEAD' !== $method ) {
return false;
} // POST, PUT, DELETE
if ( ! ( $a = @parse_url( $a ) ) ) {
return false;
}
if ( ! ( $b = @parse_url( $b ) ) ) {
return false;
}
// leave scheme to site configuration because is_ssl() doesn’t work behind some load balancers.
unset( $a['scheme'] );
unset( $b['scheme'] );
// $_SERVER['HTTP_HOST'] can't be available in case of malicious url.
$key = isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : '';
if ( empty( $a['host'] ) ) {
$a['host'] = $key;
}
if ( empty( $b['host'] ) ) {
$b['host'] = $key;
}
$key = array_diff( $a, $b );
return empty( $key );
}
/**
* Explod with multiple delimiter.
*
* @param $delimiters
* @param $string
*
* @return array|false|string[]
*/
public static function multiexplode( $delimiters, $string ) {
return is_array( $string ) ? $string : array_filter( explode( $delimiters[0], str_replace( $delimiters, $delimiters[0], $string ) ) );
}
/**
* HTML/XHTML filter that only allows some elements and attributes
*
* @param $str
* @param bool $allow_tags
*
* @return string
* @see wp-includes/kses.php
*/
public static function kses( $str, $allow_tags = true ) {
is_array( $allow_tags ) or $allow_tags = ( $allow_tags ? $GLOBALS['allowedtags'] : array() );
// wp_kses() is unavailable on advanced-cache.php
return wp_kses( $str, $allow_tags );
}
/**
* Retrieve nonce from queries or referrer
*
* @param $key
*
* @return string|string[]|null
*/
public static function retrieve_nonce( $key ) {
if ( isset( $_REQUEST[ $key ] ) ) {
return preg_replace( '/[^\w]/', '', $_REQUEST[ $key ] );
}
if ( preg_match( "/$key(?:=|%3D)([\w]+)/", self::get_referer(), $matches ) ) {
return preg_replace( '/[^\w]/', '', $matches[1] );
}
return null;
}
/**
* Trace nonce
*
* @param $nonce
*/
public static function trace_nonce( $nonce ) {
if ( self::is_user_logged_in() && empty( $_REQUEST[ $nonce ] ) &&
self::retrieve_nonce( $nonce ) && 'GET' === IP_Location_Block_Util::get_request_method() ) {
// add nonce at add_admin_nonce() to handle the client side redirection.
self::redirect( esc_url_raw( $_SERVER['REQUEST_URI'] ), 302 );
exit;
}
}
/**
* Retrieve or remove nonce and rebuild query strings.
*
* @param $location
* @param bool $retrieve
*
* @return string
*/
public static function rebuild_nonce( $location, $retrieve = true ) {
// check if the location is internal
$url = parse_url( $location );
$key = IP_Location_Block::get_auth_key();
if ( empty( $url['host'] ) || $url['host'] === parse_url( home_url(), PHP_URL_HOST ) ) {
if ( $retrieve ) {
// it doesn't care a nonce is valid or not, but must be sanitized
if ( $nonce = self::retrieve_nonce( $key ) ) {
return esc_url_raw( add_query_arg(
array(
$key => false, // delete onece
$key => $nonce // add again
),
$location
) );
}
} else {
// remove a nonce from existing query
$location = esc_url_raw( add_query_arg( $key, false, $location ) );
wp_parse_str( isset( $url['query'] ) ? $url['query'] : '', $query );
$args = array();
foreach ( $query as $arg => $val ) { // $val is url decoded
if ( false !== strpos( $val, $key ) ) {
$val = urlencode( add_query_arg( $key, false, $val ) );
}
$args[] = "$arg=$val";
}
$url['query'] = implode( '&', $args );
return self::unparse_url( $url );
}
}
return $location;
}
/**
* Convert back to string from a parsed url.
*
* @source https://php.net/manual/en/function.parse-url.php#106731
* @param $url
*
* @return string
*/
private static function unparse_url( $url ) {
$scheme = ! empty( $url['scheme'] ) ? $url['scheme'] . '://' : '';
$host = ! empty( $url['host'] ) ? $url['host'] : '';
$port = ! empty( $url['port'] ) ? ':' . $url['port'] : '';
$user = ! empty( $url['user'] ) ? $url['user'] : '';
$pass = ! empty( $url['pass'] ) ? ':' . $url['pass'] : '';
$pass = ( $user || $pass ) ? $pass . '@' : '';
$path = ! empty( $url['path'] ) ? $url['path'] : '';
$query = ! empty( $url['query'] ) ? '?' . $url['query'] : '';
$fragment = ! empty( $url['fragment'] ) ? '#' . $url['fragment'] : '';
return "$scheme$user$pass$host$port$path$query$fragment";
}
/**
* WP alternative function of wp_create_nonce() for mu-plugins
*
* Creates a cryptographic tied to the action, user, session, and time.
* @source wp-includes/pluggable.php
*
* @param int $action
*
* @return false|string
*/
public static function create_nonce( $action = - 1 ) {
$uid = self::get_current_user_id();
$tok = self::get_session_token();
$exp = self::nonce_tick();
return substr( self::hash_nonce( $exp . '|' . $action . '|' . $uid . '|' . $tok ), - 12, 10 );
}
/**
* WP alternative function of wp_verify_nonce() for mu-plugins
*
* Verify that correct nonce was used with time limit.
* @source wp-includes/pluggable.php
*
* @param $nonce
* @param int $action
*
* @return false|int
*/
public static function verify_nonce( $nonce, $action = - 1 ) {
$uid = self::get_current_user_id();
$tok = self::get_session_token();
$exp = self::nonce_tick();
// Nonce generated 0-12 hours ago
$expected = substr( self::hash_nonce( $exp . '|' . $action . '|' . $uid . '|' . $tok ), - 12, 10 );
if ( self::hash_equals( $expected, (string) $nonce ) ) {
return 1;
}
// Nonce generated 12-24 hours ago
$expected = substr( self::hash_nonce( ( $exp - 1 ) . '|' . $action . '|' . $uid . '|' . $tok ), - 12, 10 );
if ( self::hash_equals( $expected, (string) $nonce ) ) {
return 2;
}
// Invalid nonce
return false;
}
/**
* WP alternative function of wp_hash() for mu-plugins
*
* Get hash of given string for nonce.
* @source wp-includes/pluggable.php
*
* @param $data
* @param string $scheme
*
* @return false|mixed|string
*/
private static function hash_nonce( $data, $scheme = 'nonce' ) {
$salt = array(
'auth' => AUTH_KEY . AUTH_SALT,
'secure_auth' => SECURE_AUTH_KEY . SECURE_AUTH_SALT,
'logged_in' => LOGGED_IN_KEY . LOGGED_IN_SALT,
'nonce' => NONCE_KEY . NONCE_SALT,
);
return self::hash_hmac( 'md5', $data, apply_filters( 'salt', $salt[ $scheme ], $scheme ) );
}
/**
* WP alternative function for mu-plugins
*
* Retrieve the current session token from the logged_in cookie.
* @source wp-includes/user.php
*/
private static function get_session_token() {
// Arrogating logged_in cookie never cause the privilege escalation.
$cookie = self::parse_auth_cookie( 'logged_in' );
return ! empty( $cookie['token'] ) ? $cookie['token'] : NONCE_KEY . NONCE_SALT;
}
/**
* WP alternative function for mu-plugins
*
* Parse a cookie into its components. It assumes the key including $scheme.
* @source wp-includes/pluggable.php
*
* @param string $scheme
*
* @return array|false|null
*/
private static function parse_auth_cookie( $scheme = 'logged_in' ) {
if(!defined('COOKIEHASH')) {
wp_cookie_constants();
}
switch ( $scheme ) {
case 'auth':
$cookie_name = AUTH_COOKIE;
break;
case 'secure_auth':
$cookie_name = SECURE_AUTH_COOKIE;
break;
case 'logged_in':
$cookie_name = LOGGED_IN_COOKIE;
break;
default:
if ( is_ssl() ) {
$cookie_name = SECURE_AUTH_COOKIE;
$scheme = 'secure_auth';
} else {
$cookie_name = AUTH_COOKIE;
$scheme = 'auth';
}
}
if ( empty( $_COOKIE[ $cookie_name ] ) ) {
return false;
}
$cookie = $_COOKIE[ $cookie_name ];
$cookie_elements = explode( '|', $cookie );
if ( count( $cookie_elements ) !== 4 ) {
return false;
}
list( $username, $expiration, $token, $hmac ) = $cookie_elements;
return compact( 'username', 'expiration', 'token', 'hmac', 'scheme' );
}
/**
* WP alternative function for mu-plugins
*
* Retrieve user info by a given field
* @source wp-includes/pluggable.php @since 0.2.8.0
*
* @param $field
* @param $value
*
* @return false|WP_User
*/
public static function get_user_by( $field, $value ) {
if ( function_exists( 'get_user_by' ) ) {
return get_user_by( $field, $value );
}
$userdata = WP_User::get_data_by( $field, $value ); // wp-includes/class-wp-user.php @since 3.3.0
if ( ! $userdata ) {
return false;
}
$user = new WP_User;
$user->init( $userdata );
return $user;
}
/**
* WP alternative function for mu-plugins
*
* Validates authentication cookie.
* @source wp-includes/pluggable.php
*
* @param string $scheme
*
* @return false|WP_User
*/
public static function validate_auth_cookie( $scheme = '' ) {
$cookie_elements = self::parse_auth_cookie( $scheme );
if ( ! $cookie_elements ) {
return false;
}
$scheme = $cookie_elements['scheme'];
$username = $cookie_elements['username'];
$hmac = $cookie_elements['hmac'];
$token = $cookie_elements['token'];
$expiration = $cookie_elements['expiration'];
$expired = (int) $expiration;
// Allow a grace period for POST and Ajax requests.
if ( wp_doing_ajax() || 'POST' === $_SERVER['REQUEST_METHOD'] ) {
$expired += HOUR_IN_SECONDS;
}
// Quick check to see if an honest cookie has expired.
if ( $expired < time() ) {
return false;
}
$user = self::get_user_by( 'login', $username );
if ( ! $user ) {
return false;
}
if ( str_starts_with( $user->user_pass, '$P$' ) || str_starts_with( $user->user_pass, '$2y$' ) ) {
// Retain previous behaviour of phpass or vanilla bcrypt hashed passwords.
$pass_frag = substr( $user->user_pass, 8, 4 );
} else {
// Otherwise, use a substring from the end of the hash to avoid dealing with potentially long hash prefixes.
$pass_frag = substr( $user->user_pass, -4 );
}
$key = self::hash_nonce( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
$hash = self::hash_hmac( 'sha256', $username . '|' . $expiration . '|' . $token, $key );
if ( ! self::hash_equals( $hash, $hmac ) ) {
return false;
}
if ( class_exists( '\WP_Session_Tokens' ) ) {
$manager = WP_Session_Tokens::get_instance( $user->ID );
if ( ! $manager->verify( $token ) ) {
return false;
}
}
// Ajax/POST grace period set above.
if ( $expiration < time() ) {
$GLOBALS['login_grace_period'] = 1;
}
return $user;
}
/**
* WP alternative function for mu-plugins
*
* Get the time-dependent variable for nonce creation.
* @source wp_nonce_tick() in wp-includes/pluggable.php
*/
private static function nonce_tick() {
return ceil( time() / ( apply_filters( 'nonce_life', DAY_IN_SECONDS ) / 2 ) );
}
/**
* WP alternative function of hash_equals() for mu-plugins
*
* Timing attack safe string comparison.
* @source https://php.net/manual/en/function.hash-equals.php#115635
* @see https://php.net/manual/en/language.operators.increment.php
* @see wp-includes/compat.php
*
* @param $a
* @param $b
*
* @return bool
*/
private static function hash_equals( $a, $b ) {
// PHP 5 >= 5.6.0 or wp-includes/compat.php
if ( function_exists( 'hash_equals' ) ) {
return hash_equals( $a, $b );
}
if ( ( $i = strlen( $a ) ) !== strlen( $b ) ) {
return false;
}
$exp = $a ^ $b; // length of both $a and $b are same
$ret = 0;
while ( -- $i >= 0 ) {
$ret |= ord( $exp[ $i ] );
}
return ! $ret;
}
/**
* WP alternative function of hash_hmac() for mu-plugins
*
* Generate a keyed hash value using the HMAC method.
* @source https://php.net/manual/en/function.hash-hmac.php#93440
*
* @param $algo
* @param $data
* @param $key
* @param bool $raw_output
*
* @return false|mixed|string
*/
public static function hash_hmac( $algo, $data, $key, $raw_output = false ) {
// PHP 5 >= 5.1.2, PECL hash >= 1.1 or wp-includes/compat.php
if ( function_exists( 'hash_hmac' ) ) {
return hash_hmac( $algo, $data, $key, $raw_output );
}
$packs = array( 'md5' => 'H32', 'sha1' => 'H40' );
if ( ! isset( $packs[ $algo ] ) ) {
return false;
}
$pack = $packs[ $algo ];
if ( strlen( $key ) > 64 ) {
$key = pack( $pack, $algo( $key ) );
}
$key = str_pad( $key, 64, chr( 0 ) );
$ipad = ( substr( $key, 0, 64 ) ^ str_repeat( chr( 0x36 ), 64 ) );
$opad = ( substr( $key, 0, 64 ) ^ str_repeat( chr( 0x5C ), 64 ) );
$hmac = $algo( $opad . pack( $pack, $algo( $ipad . $data ) ) );
return $raw_output ? pack( $pack, $hmac ) : $hmac;
}
/**
* WP alternative function of wp_sanitize_redirect() for mu-plugins
*
* Sanitizes a URL for use in a redirect.
* @source wp-includes/pluggable.php
*
* @param $matches
*
* @return string
*/
private static function sanitize_utf8_in_redirect( $matches ) {
return urlencode( $matches[0] );
}
/**
* Sanitize redirect
*
* @param $location
*
* @return string|string[]
*/
private static function sanitize_redirect( $location ) {
$regex = '/
(
(?: [\xC2-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx
| \xE0[\xA0-\xBF][\x80-\xBF] # triple-byte sequences 1110xxxx 10xxxxxx * 2
| [\xE1-\xEC][\x80-\xBF]{2}
| \xED[\x80-\x9F][\x80-\xBF]
| [\xEE-\xEF][\x80-\xBF]{2}
| \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences 11110xxx 10xxxxxx * 3
| [\xF1-\xF3][\x80-\xBF]{3}
| \xF4[\x80-\x8F][\x80-\xBF]{2}
){1,40} # ...one or more times
)/x';
$location = preg_replace_callback( $regex, array( __CLASS__, 'sanitize_utf8_in_redirect' ), $location );
$location = preg_replace( '|[^a-z0-9-~+_.?#=&;,/:%!*\[\]()@]|i', '', $location );
$location = self::kses_no_null( $location ); // wp-includes/kses.php
// remove %0d and %0a from location
$strip = array( '%0d', '%0a', '%0D', '%0A' );
return self::deep_replace( $strip, $location ); // wp-includes/formatting.php
}
/**
* WP alternative function of wp_redirect() for mu-plugins
*
* Redirects to another page.
* @source wp-includes/pluggable.php
*
* @param $location
* @param int $status
*
* @return bool
*/
public static function redirect( $location, $status = 302 ) {
// retrieve nonce from referer and add it to the location
$location = self::rebuild_nonce( $location, true );
$location = self::sanitize_redirect( $location );
if ( $location ) {
if ( ! self::is_IIS() && PHP_SAPI != 'cgi-fcgi' ) {
status_header( $status );
} // This causes problems on IIS and some FastCGI setups
header( "Location: $location", true, $status );
return true;
} else {
return false;
}
}
/**
* WP alternative function of wp_redirect() for mu-plugins
*
* Performs a safe (local) redirect, using redirect().
* @source wp-includes/pluggable.php
*
* @param $location
* @param int $status
*/
public static function safe_redirect( $location, $status = 302 ) {
// Need to look at the URL the way it will end up in wp_redirect()
$location = self::sanitize_redirect( $location );
// Filters the redirect fallback URL for when the provided redirect is not safe (local).
$location = self::validate_redirect( $location, apply_filters( 'wp_safe_redirect_fallback', admin_url(), $status ) );
self::redirect( $location, $status );
}
/**
* WP alternative function of wp_validate_redirect() for mu-plugins
*
* Validates a URL for use in a redirect.
* @source wp-includes/pluggable.php
*
* @param $location
* @param string $default
*
* @return mixed|string
*/
private static function validate_redirect( $location, $default = '' ) {
// browsers will assume 'http' is your protocol, and will obey a redirect to a URL starting with '//'
if ( substr( $location = trim( $location ), 0, 2 ) == '//' ) {
$location = 'http:' . $location;
}
// In php 5 parse_url may fail if the URL query part contains http://, bug #38143
$test = ( $cut = strpos( $location, '?' ) ) ? substr( $location, 0, $cut ) : $location;
// @-operator is used to prevent possible warnings in PHP < 5.3.3.
$lp = @parse_url( $test );
// Give up if malformed URL
if ( false === $lp ) {
return $default;
}
// Allow only http and https schemes. No data:, etc.
if ( isset( $lp['scheme'] ) && ! ( 'http' == $lp['scheme'] || 'https' == $lp['scheme'] ) ) {
return $default;
}
// Reject if certain components are set but host is not. This catches urls like https:host.com for which parse_url does not set the host field.
if ( ! isset( $lp['host'] ) && ( isset( $lp['scheme'] ) || isset( $lp['user'] ) || isset( $lp['pass'] ) || isset( $lp['port'] ) ) ) {
return $default;
}
// Reject malformed components parse_url() can return on odd inputs.
foreach ( array( 'user', 'pass', 'host' ) as $component ) {
if ( isset( $lp[ $component ] ) && strpbrk( $lp[ $component ], ':/?#@' ) ) {
return $default;
}
}
return $location;
}
/**
* WP alternative function of wp_get_raw_referer() for mu-plugins
*
* Retrieves unvalidated referer from '_wp_http_referer' or HTTP referer.
* @source wp-includes/functions.php
* @uses wp_unslash() can be replaced with stripslashes() in this context because the target value is 'string'.
*/
private static function get_raw_referer() {
if ( ! empty( $_REQUEST['_wp_http_referer'] ) ) {
return /*wp_unslash*/ stripslashes( $_REQUEST['_wp_http_referer'] );
} // wp-includes/formatting.php
elseif ( ! empty( $_SERVER['HTTP_REFERER'] ) ) {
return /*wp_unslash*/ stripslashes( $_SERVER['HTTP_REFERER'] );
} // wp-includes/formatting.php
return false;
}
/**
* WP alternative function of wp_get_referer() for mu-plugins
*
* Retrieve referer from '_wp_http_referer' or HTTP referer.
* @source wp-includes/functions.php
*/
public static function get_referer() {
$ref = self::get_raw_referer(); // wp-includes/functions.php
$req = /*wp_unslash*/
stripslashes( $_SERVER['REQUEST_URI'] );
if ( $ref && $ref !== $req && $ref !== home_url() . $req ) {
return self::validate_redirect( $ref, false );
}
return false;
}
/**
* WP alternative function of is_user_logged_in() for mu-plugins
*
* Checks if the current visitor is a logged in user.
* @source wp-includes/pluggable.php
*/
public static function is_user_logged_in() {
if ( function_exists( 'is_user_logged_in' ) || did_action( 'init' ) ) {
$logged_in = is_user_logged_in();
} else {
$settings = IP_Location_block::get_option();
$timing_off = isset( $settings['validation']['timing'] ) ? ( 0 === ( (int) $settings['validation']['timing'] ) ) : 0;
if ( $timing_off ) {
$logged_in = function_exists( 'is_user_logged_in' ) && is_user_logged_in(); // @since 0.2.0.0
} else {
$user = self::validate_auth_cookie('logged_in');
$logged_in = $user ? $user->exists() : false; // @since 3.4.0
}
}
return $logged_in;
}
/**
* WP alternative function of get_current_user_id() for mu-plugins
*
* Get the current user's ID.
* @source wp-includes/user.php
*/
public static function get_current_user_id() {
static $user_id = null;
if ( did_action( 'init' ) ) {
$user_id = get_current_user_id(); // @since MU 3.0.0
} else {
$user = self::validate_auth_cookie('logged_in');
$user_id = $user ? $user->ID : 0; // @since 0.2.0.0
}
return $user_id;
}
/**
* WP alternative function current_user_can() for mu-plugins
*
* Whether the current user has a specific capability.
* @source wp-includes/capabilities.php
*
* @param $capability
*
* @return bool
*/
public static function current_user_can( $capability ) {
if ( did_action( 'init' ) ) {
return current_user_can( $capability );
} // @since 0.2.0.0
return ( $user = self::validate_auth_cookie('logged_in') ) ? $user->has_cap( $capability ) : false; // @since 0.2.0.0
}
/**
* Check if the current user has the capabilities.
*
* @param $caps
*
* @return bool
*/
public static function current_user_has_caps( $caps ) {
$user = self::get_user_by( 'id', self::get_current_user_id() );
if ( is_object( $user ) ) {
foreach ( $caps as $cap ) {
if ( $user->has_cap( $cap ) ) {
return true;
}
}
}
return false;
}
/**
* WP alternative function get_allowed_mime_types() for mu-plugins
*
* Retrieve the file type from the file name.
* @source wp-includes/functions.php @since 0.2.0.4
*
* @param null $user
*
* @return mixed|void
*/
public static function get_allowed_mime_types( $user = null ) {
$type = wp_get_mime_types();
unset( $type['swf'], $type['exe'] );
if ( ! self::current_user_can( 'unfiltered_html' ) ) {
unset( $type['htm|html'] );
}
return apply_filters( 'upload_mimes', $type, $user );
}
/**
* WP alternative function wp_check_filetype_and_ext() for mu-plugins
*
* Attempt to determine the real file type of a file.
* @source wp-includes/functions.php @since 3.0.0
*
* @param $fileset
* @param $mode
* @param $mimeset
*
* @return bool
*/
public static function check_filetype_and_ext( $fileset, $mode, $mimeset ) {
$src = @$fileset['tmp_name'];
$dst = str_replace( "\0", '', urldecode( @$fileset['name'] ) );
// We can't do any further validation without a file to work with
if ( ! @file_exists( $src ) ) {
return true;
}
// check extension at the tail in blacklist
if ( 2 === (int) $mode ) {
$type = pathinfo( $dst, PATHINFO_EXTENSION );
if ( $type && false !== stripos( $mimeset['black_list'], $type ) ) {
return false;
}
}
// check extension at the tail in whitelist
$type = wp_check_filetype( $dst, $mimeset['white_list'] );
if ( 1 === (int) $mode ) {
if ( ! $type['type'] ) {
return false;
}
}
// check images using GD (it doesn't care about extension if it's a real image file)
if ( 0 === strpos( $type['type'], 'image/' ) && function_exists( 'getimagesize' ) ) {
$info = @getimagesize( $src ); // 0:width, 1:height, 2:type, 3:string
if ( ! $info || $info[0] > 9000 || $info[1] > 9000 ) { // max: EOS 5Ds
return false;
}
}
return true;
}
/**
* Arrange $_FILES array
*
* @see https://php.net/manual/features.file-upload.multiple.php#53240
*
* @param $files
*
* @return array
*/
public static function arrange_files( $files ) {
if ( ! is_array( $files['name'] ) ) {
return array( $files );
}
$file_array = array();
$file_count = count( $files['name'] );
$file_keys = array_keys( $files );
for ( $i = 0; $i < $file_count; ++ $i ) {
foreach ( $file_keys as $key ) {
$file_array[ $i ][ $key ] = $files[ $key ][ $i ];
}
}
return $file_array;
}
/**
*
* Remove slash at the end of string.
* @source wp-includes/formatting.php
*
* @param $string
*
* @return string
*/
public static function unslashit( $string ) {
if ( ! is_string( $string ) ) {
return $string;
}
return rtrim( $string, '/\\' );
}
/**
* Add slash at the end of string.
*
* @param $string
*
* @return string
*/
public static function slashit( $string ) {
return rtrim( $string, '/\\' ) . '/';
}
/**
* WP alternative function of wp_kses_no_null() for advanced-cache.php
*
* Removes any NULL characters in $string.
* @source wp-includes/kses.php
*
* @param $string
*
* @return string|string[]|null
*/
private static function kses_no_null( $string ) {
$string = preg_replace( '/[\x00-\x08\x0B\x0C\x0E-\x1F]/', '', $string );
$string = preg_replace( '/\\\\+0+/', '', $string );
return $string;
}
/**
* WP alternative function of _deep_replace() for advanced-cache.php
*
* Perform a deep string replace operation to ensure the values in $search are no longer present.
* e.g. $subject = '%0%0%0DDD', $search ='%0D', $result ='' rather than the '%0%0DD' that str_replace would return
* @source wp-includes/formatting.php
*
* @param $search
* @param $subject
*
* @return string|string[]
*/
private static function deep_replace( $search, $subject ) {
$subject = (string) $subject;
$count = 1;
while ( $count ) {
$subject = str_replace( $search, '', $subject, $count );
}
return $subject;
}
/**
* Remove `HOST` and `HOST=...` from `UA and qualification`
*
* @param $ua_list
*
* @return string|string[]|null
*/
public static function mask_qualification( $ua_list ) {
return preg_replace( array( '/HOST[^,]*?/', '/\*[:#]!?\*,?/' ), array( '*', '' ), $ua_list );
}
/**
* https://codex.wordpress.org/WordPress_Feeds
* @param $request_uri
*
* @return bool
*/
public static function is_feed( $request_uri ) {
return /* function_exists( 'is_feed' ) ? is_feed() : */ ( isset( $_GET['feed'] ) ?
( preg_match( '!(?:comments-)?(?:feed|rss|rss2|rdf|atom)$!', $_GET['feed'] ) ? true : false ) :
( preg_match( '!(?:comments/)?(?:feed|rss|rss2|rdf|atom)/?$!', $request_uri ) ? true : false )
);
}
/**
* Whether the server software is IIS or something else
*
* @source wp-includes/vers.php
*/
private static function is_IIS() {
$_is_apache = ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Apache' ) !== false || strpos( $_SERVER['SERVER_SOFTWARE'], 'LiteSpeed' ) !== false );
$_is_IIS = ! $_is_apache && ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS' ) !== false || strpos( $_SERVER['SERVER_SOFTWARE'], 'ExpressionDevServer' ) !== false );
return $_is_IIS ? substr( $_SERVER['SERVER_SOFTWARE'], strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/' ) + 14 ) : false;
}
/**
* Check proxy variable
*
* @see https://developer.wordpress.org/reference/classes/wp_community_events/get_unsafe_client_ip/
*/
public static function get_proxy_var() {
foreach (
array(
'HTTP_X_FORWARDED_FOR',
'HTTP_CF_CONNECTING_IP',
'HTTP_X_REAL_IP',
'HTTP_CLIENT_IP',
'HTTP_X_FORWARDED',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED'
) as $var
) {
if ( isset( $_SERVER[ $var ] ) ) {
return $var;
}
}
return null;
}
/**
* Pick up all the IPs in HTTP_X_FORWARDED_FOR, HTTP_CLIENT_IP and etc.
*
* @param array $ips array of candidate IP addresses
* @param string $vars comma separated keys in $_SERVER for http header ('HTTP_...')
*
* @return array $ips array of candidate IP addresses
*/
public static function retrieve_ips( $ips = array(), $vars = null ) {
if ( is_null( $vars ) ) {
return [];
}
foreach ( explode( ',', $vars ) as $var ) {
if ( isset( $_SERVER[ $var ] ) ) {
foreach ( explode( ',', $_SERVER[ $var ] ) as $ip ) {
if ( ! in_array( $ip = trim( $ip ), $ips, true ) && ! self::is_private_ip( $ip ) ) {
array_unshift( $ips, $ip );
}
}
}
}
return $ips;
}
/**
* Get client IP address
*
* @param string $vars comma separated keys in $_SERVER for http header ('HTTP_...')
*
* @return string $ip IP address
* @link https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/x-forwarded-headers.html
* @link https://github.com/zendframework/zend-http/blob/master/src/PhpEnvironment/RemoteAddress.php
*/
public static function get_client_ip( $vars = null ) {
foreach ( explode( ',', $vars ) as $var ) {
if ( isset( $_SERVER[ $var ] ) ) {
$ips = array_map( 'trim', explode( ',', $_SERVER[ $var ] ) );
while ( $var = array_pop( $ips ) ) {
if ( ! self::is_private_ip( $var ) ) {
return $var;
}
}
}
}
return isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1'; // for CLI
}
/**
* Check the client IP address behind the VPN proxy
*
*/
public static function get_proxy_ip( $ip ) {
// Chrome datasaver
if ( isset( $_SERVER['HTTP_VIA'], $_SERVER['HTTP_FORWARDED'] ) && false !== strpos( $_SERVER['HTTP_VIA'], 'Chrome-Compression-Proxy' ) ) {
// require_once IP_LOCATION_BLOCK_PATH . 'classes/class-ip-location-block-lkup.php';
// if ( FALSE !== strpos( 'google', IP_Location_Block_Lkup::gethostbyaddr( $ip ) ) )
$proxy = preg_replace( '/^for=.*?([a-f\d\.:]+).*$/', '$1', $_SERVER['HTTP_FORWARDED'] );
} // Puffin browser
elseif ( isset( $_SERVER['HTTP_X_PUFFIN_UA'], $_SERVER['HTTP_USER_AGENT'] ) && false !== strpos( $_SERVER['HTTP_USER_AGENT'], 'Puffin' ) ) {
$proxy = explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] );
$proxy = trim( end( $proxy ) ); // or trim( $proxy[0] )
}
return empty( $proxy ) ? $ip : $proxy;
}
/**
* Returns the request method
* @return mixed|string
*/
public static function get_request_method() {
return isset( $_SERVER['REQUEST_METHOD'] ) ? $_SERVER['REQUEST_METHOD'] : '';
}
/**
* Check the IP address is private or not
*
* @link https://en.wikipedia.org/wiki/Localhost
* @link https://en.wikipedia.org/wiki/Private_network
* @link https://en.wikipedia.org/wiki/Reserved_IP_addresses
*
* 10.0.0.0/8 reserved for Private-Use Networks [RFC1918]
* 127.0.0.0/8 reserved for Loopback [RFC1122]
* 172.16.0.0/12 reserved for Private-Use Networks [RFC1918]
* 192.168.0.0/16 reserved for Private-Use Networks [RFC1918]
*
* @param $ip
*
* @return bool
*/
public static function is_private_ip( $ip ) {
// https://php.net/manual/en/filter.filters.flags.php
return ! filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE );
}
/**
* Get IP address of the host server
*
* @link https://php.net/manual/en/reserved.variables.server.php#88418
*/
public static function get_server_ip() {
return isset( $_SERVER['SERVER_ADDR'] ) ? $_SERVER['SERVER_ADDR'] : ( (int) self::is_IIS() >= 7 ?
( isset( $_SERVER['LOCAL_ADDR'] ) ? $_SERVER['LOCAL_ADDR'] : null ) : null );
}
/**
* Get the list of registered actions
*
* @param bool $ajax
*
* @return array
*/
public static function get_registered_actions( $ajax = false, $settings = [] ) {
$installed = array();
$default_actions = self::allowed_pages_actions( $settings );
global $wp_filter;
foreach ( $wp_filter as $key => $val ) {
$mod = 0;
if ( $ajax && false !== strpos( $key, 'wp_ajax_' ) ) {
if ( 0 === strpos( $key, 'wp_ajax_nopriv_' ) ) {
$key = substr( $key, 15 ); // 'wp_ajax_nopriv_'
$val = 2; // without privilege
} else {
$key = substr( $key, 8 ); // 'wp_ajax_'
$val = 1; // with privilege
}
$mod = 1;
} elseif ( false !== strpos( $key, 'admin_post_' ) ) {
if ( 0 === strpos( $key, 'admin_post_nopriv_' ) ) {
$key = substr( $key, 18 ); // 'admin_post_nopriv_'
$val = 2; // without privilege
} else {
$key = substr( $key, 11 ); // 'admin_post_'
$val = 1; // with privilege
}
$mod = 1;
}
if($mod) {
$include = 0;
foreach($default_actions as $default_action) {
if($default_action === $key || self::wildcard_match($default_action, $key)) {
$include = 1;
break;
}
}
if($include) {
$installed[ $key ] = isset( $installed[ $key ] ) ? $installed[ $key ] | $val : $val;
}
}
}
if ( isset( $installed['ip_location_block'] ) ) {
unset( $installed['ip_location_block'] );
}
return $installed;
}
/**
* Get the list of multisite managed by the specific user
*
* This function should be called after 'init' hook is fired.
*/
public static function get_sites_of_user() {
$sites = array( preg_replace( '/^https?:/', '', home_url() ) );
foreach ( get_blogs_of_user( self::get_current_user_id(), current_user_can( 'manage_network_options' ) ) as $site ) { // @since 3.0.0
if ( ! in_array( $url = preg_replace( '/^https?:/', '', $site->siteurl ), $sites, true ) ) {
$sites[] = $url;
}
}
return $sites;
}
/**
* Anonymize IP address in string
*
* @param $subject
* @param bool $strict
*
* @return string|string[]|null
*/
public static function anonymize_ip( $subject, $strict = true ) {
return $strict ?
preg_replace( '/(:)*[0-9a-f\*]{0,4}$/', '$1***', $subject, 1 ) :
preg_replace(
array(
'/([0-9a-f]{3,})[0-9a-f]{3,}/', // loose pattern for IPv[4|6]
'/((?:[0-9]{1,3}[-_x\.]){3,})[0-9]+/', // loose pattern for IPv4
'/((?:[0-9a-f]+[-:]+)+)[0-9a-f:\*]{2,}/', // loose pattern for IPv6
),
'$1***',
$subject
);
}
/**
* Generates cryptographically secure pseudo-random bytes
*
* @param int $length
*
* @return string|null
*/
private static function random_bytes( $length = 64 ) {
if ( version_compare( PHP_VERSION, '7.0.0', '<' ) ) {
require_once IP_LOCATION_BLOCK_PATH . 'includes/random_compat/random.php';
}
// align length
$length = max( 64, $length ) - ( $length % 2 );
try {
$str = bin2hex( random_bytes( $length / 2 ) );
} catch ( \Exception $e ) {
$str = null;
}
if ( empty( $str ) && function_exists( 'openssl_random_pseudo_bytes' ) ) {
$str = bin2hex( openssl_random_pseudo_bytes( $length / 2 ) );
}
if ( empty( $str ) ) {
for ( $i = 0; $i < $length; $i ++ ) {
$str .= chr( ( mt_rand( 1, 36 ) <= 26 ) ? mt_rand( 97, 122 ) : mt_rand( 48, 57 ) );
}
}
return $str;
}
/**
* Manipulate emergency login link
*
* @param $link
*
* @return false|mixed|string
*/
private static function hash_link( $link ) {
return self::hash_hmac(
function_exists( 'hash' ) ? 'sha256' /* 32 bytes (256 bits) */ : 'sha1' /* 20 bytes (160 bits) */,
$link, NONCE_SALT, true
);
}
// used at `admin_ajax_callback()` in class-ip-location-block-admin.php
public static function generate_link( $context ) {
$link = self::random_bytes();
$hash = bin2hex( self::hash_link( $link ) );
/**
* Verify the consistency of `self::hash_hmac()`
* key from external: self::verify_link( $link )
* key from internal: self::verify_link( 'link', 'hash' )
*/
$settings = IP_Location_Block::get_option();
$settings['login_link'] = array(
'link' => $hash,
'hash' => bin2hex( self::hash_link( $hash ) ),
);
if ( $context->is_network_admin() && $settings['network_wide'] ) {
$context->update_multisite_settings( $settings );
} else {
IP_Location_Block::update_option( $settings );
}
return add_query_arg( 'ip-location-block-key', $link, wp_login_url() );
}
// used at `admin_ajax_callback()` in class-ip-location-block-admin.php
public static function delete_link( $context ) {
$settings = IP_Location_Block::get_option();
$settings['login_link'] = array( 'link' => null, 'hash' => null );
if ( $context->is_network_admin() && $settings['network_wide'] ) {
$context->update_multisite_settings( $settings );
} else {
IP_Location_Block::update_option( $settings );
}
}
// used at `tab_setup()` in tab-settings.php
public static function get_link() {
$settings = IP_Location_Block::get_option();
return $settings['login_link']['link'] ? $settings['login_link']['link'] : false;
}
// used at `validate_login()` in class-ip-location-block.php
public static function verify_link( $link, $hash = null ) {
return self::hash_equals( self::hash_link( $link ), pack( 'H*', $hash ? $hash : self::get_link() ) ); // hex2bin() for PHP 5.4+
}
/**
* Validates JSON string
*
* @param $string
*
* @return bool
* @since 1.3.0
*/
public static function json_validate( $string ) {
if ( function_exists( 'json_validate' ) ) {
return \json_validate( $string );
}
json_decode( $string );
return json_last_error() === JSON_ERROR_NONE;
}
/**
* Removes specific keys from array
*
* @param $array
* @param $except
* @since 1.3.0
*
* @return array
*/
public static function array_except($array = [], $except = [])
{
$newArr = [];
foreach($array as $value) {
if(in_array($value, $except)) {
continue;
}
$newArr[] = $value;
}
return $newArr;
}
/**
* Parses asn in default format.
* e.g:
* 1. AS81281 Provider Name -> AS81281
* 2. 9239 -> AS9239
* 3. AS1111 -> AS1111
*/
public static function parse_asn( $asn ) {
$asn = str_replace( 'AS', '', strtok( $asn, ' ' ) );
return sprintf( 'AS%s', $asn );
}
/**
* Wildcard match
*
* @param $needle - The string with wildcard
* @param $haystack - The full string
*
* @since 1.2.2
*
* @return bool
*/
public static function wildcard_match( $needle, $haystack ) {
if(is_null($needle) || is_null($haystack)) {
return false;
}
$needle = str_replace('/', '_', $needle);
$haystack = str_replace('/', '_', $haystack);
$regex = str_replace( array( "\*", "\?" ), array( '.*', '.' ), preg_quote( $needle ) );
return (bool) preg_match( '/^' . $regex . '$/is', $haystack );
}
/**
* Match in array with wildcard support, similar to in_array($needle, $haystack)
*
* @param $needle - Single string
* @param $haystack - List of allowed strings
*
* @return bool
*/
public static function wildcard_in_array( $needle, $haystack ) {
if ( ! is_iterable( $haystack ) ) {
return false;
}
foreach ( $haystack as $wildcard_item ) {
if ( self::wildcard_match( $wildcard_item, $needle ) ) {
return true;
}
}
return false;
}
/**
* Returns the allowed actions and pages
* @param $settings
*
* @return string[]|null
*/
public static function allowed_pages_actions( $settings = [] ) {
$allowed = apply_filters( 'ip-location-block-bypass-admins', array(), $settings );
return array_merge( $allowed, array(
// in wp-admin js/widget.js, includes/template.php, async-upload.php, plugins.php, PHP Compatibility Checker, bbPress
// To find use: grep -h -o "'wp_ajax_[^']*'" -r .
// admin-ajax.php -> Core Actions POST
//'ip-location-block',
'oembed-cache',
'image-editor',
'delete-comment',
'delete-tag',
'delete-link',
'delete-meta',
'delete-post',
'trash-post',
'untrash-post',
'delete-page',
'dim-comment',
'add-link-category',
'add-tag',
'get-tagcloud',
'get-comments',
'replyto-comment',
'edit-comment',
'add-menu-item',
'add-meta',
'add-user',
'closed-postboxes',
'hidden-columns',
'update-welcome-panel',
'menu-get-metabox',
'wp-link-ajax',
'menu-locations-save',
'menu-quick-search',
'meta-box-order',
'get-permalink',
'sample-permalink',
'inline-save',
'inline-save-tax',
'find_posts',
'widgets-order',
'save-widget',
'delete-inactive-widgets',
'set-post-thumbnail',
'date_format',
'time_format',
'wp-remove-post-lock',
'dismiss-wp-pointer',
'upload-attachment',
'get-attachment',
'query-attachments',
'save-attachment',
'save-attachment-compat',
'send-link-to-editor',
'send-attachment-to-editor',
'save-attachment-order',
'media-create-image-subsizes',
'heartbeat',
'get-revision-diffs',
'save-user-color-scheme',
'update-widget',
'query-themes',
'parse-embed',
'set-attachment-thumbnail',
'parse-media-shortcode',
'destroy-sessions',
'install-plugin',
'update-plugin',
'crop-image',
'generate-password',
'save-wporg-username',
'delete-plugin',
'search-plugins',
'search-install-plugins',
'activate-plugin',
'update-theme',
'delete-theme',
'install-theme',
'get-post-thumbnail-html',
'get-community-events',
'edit-theme-plugin-file',
'wp-privacy-export-personal-data',
'wp-privacy-erase-personal-data',
'health-check*',
'health_check*',
'toggle-auto-updates',
'send-password-reset',
'set-background-image',
'custom-background-add',
'cropped_attachment_id',
'copped_attachment_metadata',
'check_plugin_dependencies',
'generate-password',
'custom-header*',
// Gutenberg
'gutenberg',
'gutenberg*',
'wp_block*',
// admin-ajax.php -> Core Actions GET
'fetch-list',
'ajax-tag-search',
'wp-compression-test',
'imgedit-preview',
'oembed-cache',
'autocomplete-user',
'dashboard-widgets',
'logged-in',
'rest-nonce',
// wp actions
'activate',
'deactivate',
'bulk-activate',
'bulk-deactivate',
// acf
'acf*',
// Autoptimize
'fetch_critcss',
'save_critcss',
'rm_critcss',
'rm_critcss_all',
'ao_ccss*',
'autoptimize_delete_cache',
'rm_critcss',
'dismiss_admin_notice',
//bbpress
'bbp_suggest_*',
'bbp_converter_*',
//buddypress
'bp_get_*',
'bp_cover_*',
'bp_avatar_*',
'bp_dismiss_notice',
'bp-activity*',
'bp_group_*',
'widget_*',
'xprofile_*',
//jetpack
'jetpack',
'jetpack*',
// yoast
'yoast*',
'wpseo*',
'duplicate_post*',
// updraft
'updraft*',
'updraft',
'plupload*',
// Litespeed Cache
'async_litespeed',
'litespeed',
// WP Fastest Cache
'wpfc',
'wpfc*',
// Bricks
'bricks',
'bircks*',
// Elementor
'elementor_*',
'elementor',
// Beaver
'dismiss_fl_notice',
'export_global_settings',
'import_global_settings',
'reset_global_settings',
'fl_*',
// Divi
'save_epanel',
'et_*',
// WooCommerce
'woocommerce_*',
'woocommerce',
// MegaOptim
'megaoptim_*',
// Vimeify
'vimeify_*',
// Digital License Manager
'dlm_*',
// WPRocket
'rocket_*',
'*rocketcdn*',
'wp-rocket',
// WordFence
'wordfence_*',
'Wordfence',
// WPForms
'wpforms',
'wpforms*',
// GravityForms
'rb',
'rb*',
'gf',
'gf*',
// NinjaFirewall
'nfw_*',
'NinjaFirewall',
// Sucuri
'sucuriscan*',
'sucuriscan',
// Advanced Cron Manager
'acm*',
// pages
// - Anti-Malware Security and Brute-Force Firewall
// - Jetpack page & action
// - Email Subscribers & Newsletters by Icegram
// - Swift Performance
'wpephpcompat_start_test',
'GOTMLS_logintime',
'jetpack',
'authorize',
'jetpack_modules',
'atd_settings',
'es_sendemail',
'swift_performance_setup',
// Advanced Access Manager
'aam',
'aamc',
) );
}
}
// Some plugins need this when this plugin is installed as mu-plugins
if ( ! function_exists( 'get_userdata' ) ) :
/**
* Retrieve user info by user ID.
*
* @param int $user_id User ID
*
* @return WP_User|false WP_User object on success, false on failure.
* @since 0.71
*
*/
function get_userdata( $user_id ) {
return IP_Location_Block_Util::get_user_by( 'id', $user_id );
}
endif;