Skip to content

Instantly share code, notes, and snippets.

@tokkonopapa
Last active August 26, 2017 14:11
Show Gist options
  • Save tokkonopapa/1948ceb9ff42ef21e9baf2d68b04d16e to your computer and use it in GitHub Desktop.
Save tokkonopapa/1948ceb9ff42ef21e9baf2d68b04d16e to your computer and use it in GitHub Desktop.
Fix the issue that an error message would not be shown when download/read/write error happens.
<?php
/**
* IP Geo Block - Cron Class
*
* @package IP_Geo_Block
* @author tokkonopapa <[email protected]>
* @license GPL-2.0+
* @link http://www.ipgeoblock.com/
* @copyright 2013-2017 tokkonopapa
*/
class IP_Geo_Block_Cron {
/**
* Cron scheduler.
*
*/
private static function schedule_cron_job( &$update, $db, $immediate = FALSE ) {
wp_clear_scheduled_hook( IP_Geo_Block::CRON_NAME, array( $immediate ) );
if ( $update['auto'] ) {
$now = time();
$cycle = DAY_IN_SECONDS * (int)$update['cycle'];
if ( FALSE === $immediate &&
$now - (int)$db['ipv4_last'] < $cycle &&
$now - (int)$db['ipv6_last'] < $cycle ) {
$update['retry'] = 0;
$next = max( (int)$db['ipv4_last'], (int)$db['ipv6_last'] ) +
$cycle + rand( DAY_IN_SECONDS, DAY_IN_SECONDS * 6 );
} else {
++$update['retry'];
$next = $now + ( $immediate ? 0 : DAY_IN_SECONDS );
}
wp_schedule_single_event( $next, IP_Geo_Block::CRON_NAME, array( $immediate ) );
}
}
/**
* Database auto downloader.
*
* This function is called when:
* 1. Plugin is activated
* 2. WP Cron is kicked
* under the following condition:
* A. Once per site when this plugin is activated on network wide
* B. Multiple time for each blog when this plugin is individually activated
*/
public static function exec_update_db( $immediate = FALSE ) {
$settings = IP_Geo_Block::get_option();
$args = IP_Geo_Block::get_request_headers( $settings );
// extract ip address from transient API to confirm the request source
add_filter( IP_Geo_Block::PLUGIN_NAME . '-ip-addr', array( __CLASS__, 'extract_ip' ) );
// download database files (higher priority order)
foreach ( $providers = IP_Geo_Block_Provider::get_addons() as $provider ) {
if ( $geo = IP_Geo_Block_API::get_instance( $provider, $settings ) ) {
$res[ $provider ] = $geo->download( $settings[ $provider ], $args );
// re-schedule cron job
self::schedule_cron_job( $settings['update'], $settings[ $provider ], FALSE );
// update option settings
self::update_settings( $settings, array( 'update', $provider ) );
// update matching rule immediately
if ( $immediate && FALSE !== ( $stat = get_transient( IP_Geo_Block::CRON_NAME ) ) && 'done' !== $stat ) {
$validate = IP_Geo_Block::get_geolocation( NULL, array( $provider ) );
$validate = IP_Geo_Block::validate_country( NULL, $validate, $settings );
// if blocking may happen then disable validation
if ( -1 !== (int)$settings['matching_rule'] && 'passed' !== $validate['result'] &&
( empty( $_SERVER['HTTP_X_REQUESTED_FROM'] ) || FALSE === strpos( $_SERVER['HTTP_X_REQUESTED_FROM'], 'InfiniteWP' ) ) ) {
$settings['matching_rule'] = -1;
}
// setup country code if it needs to be initialized
if ( -1 === (int)$settings['matching_rule'] && 'ZZ' !== $validate['code'] ) {
$settings['matching_rule'] = 0; // white list
if ( FALSE === strpos( $settings['white_list'], $validate['code'] ) )
$settings['white_list'] .= ( $settings['white_list'] ? ',' : '' ) . $validate['code'];
// update option settings
self::update_settings( $settings, array( 'matching_rule', 'white_list' ) );
// finished to update matching rule
set_transient( IP_Geo_Block::CRON_NAME, 'done', 5 * MINUTE_IN_SECONDS );
}
}
}
}
return isset( $res ) ? $res : NULL;
}
/**
* Update setting data according to the site type.
*
*/
private static function update_settings( $src, $keys = array() ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
// for multisite (@since 3.0.0 in wp-admin/includes/plugin.php)
if ( is_plugin_active_for_network( IP_GEO_BLOCK_BASE ) ) {
global $wpdb;
$blog_ids = $wpdb->get_col( "SELECT `blog_id` FROM `$wpdb->blogs`" );
foreach ( $blog_ids as $id ) {
switch_to_blog( $id );
$dst = IP_Geo_Block::get_option();
foreach ( $keys as $key ) {
$dst[ $key ] = $src[ $key ];
}
update_option( IP_Geo_Block::OPTION_NAME, $dst );
restore_current_blog();
}
}
// for single site
else {
update_option( IP_Geo_Block::OPTION_NAME, $src );
}
}
/**
* Extract ip address from transient API.
*
*/
public static function extract_ip() {
return filter_var(
$ip_adrs = get_transient( IP_Geo_Block::CRON_NAME ), FILTER_VALIDATE_IP
) ? $ip_adrs : $_SERVER['REMOTE_ADDR'];
}
/**
* Kick off a cron job to download database immediately in background on activation.
*
*/
public static function start_update_db( $settings, $force = FALSE ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
// the status is still inactive when this plugin is activated on dashboard.
if ( ! ( is_plugin_active ( IP_GEO_BLOCK_BASE ) || // @since 2.5.0
is_plugin_active_for_network( IP_GEO_BLOCK_BASE ) ) || $force ) { // @since 3.0.0
set_transient( IP_Geo_Block::CRON_NAME, IP_Geo_Block::get_ip_address(), MINUTE_IN_SECONDS );
self::schedule_cron_job( $settings['update'], NULL, TRUE );
}
}
public static function stop_update_db() {
wp_clear_scheduled_hook( IP_Geo_Block::CRON_NAME, array( FALSE ) ); // @since 2.1.0
}
/**
* Kick off a cron job to garbage collection for IP address cache.
*
* Note: When the init action occurs in /wp-settings.php, wp_cron() runs.
*/
public static function exec_cache_gc( $settings ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
if ( is_plugin_active_for_network( IP_GEO_BLOCK_BASE ) ) {
global $wpdb;
$blog_ids = $wpdb->get_col( "SELECT `blog_id` FROM `$wpdb->blogs`" );
foreach ( $blog_ids as $id ) {
switch_to_blog( $id );
IP_Geo_Block_Logs::delete_expired_cache( $settings['cache_time'] );
restore_current_blog();
}
}
// for single site
else {
IP_Geo_Block_Logs::delete_expired_cache( $settings['cache_time'] );
}
self::stop_cache_gc();
self::start_cache_gc( $settings );
}
public static function start_cache_gc( $settings ) {
if ( ! wp_next_scheduled( IP_Geo_Block::CACHE_NAME ) )
wp_schedule_single_event( time() + $settings['cache_time_gc'], IP_Geo_Block::CACHE_NAME );
}
public static function stop_cache_gc() {
wp_clear_scheduled_hook( IP_Geo_Block::CACHE_NAME ); // @since 2.1.0
}
/**
* Download zip/gz file, uncompress and save it to specified file
*
* @param string $url URL of remote file to be downloaded.
* @param array $args request headers.
* @param string $filename full path to the downloaded file.
* @param int $modified time of last modified on the remote server.
* @return array status message.
*/
public static function download_zip( $url, $args, $filename, $modified ) {
require_once IP_GEO_BLOCK_PATH . 'classes/class-ip-geo-block-file.php';
$fs = IP_Geo_Block_FS::init( 'download_zip' );
// if the name of src file is changed, then update the dst
if ( basename( $filename ) !== ( $base = pathinfo( $url, PATHINFO_FILENAME ) ) )
$filename = dirname( $filename ) . '/' . $base;
// check file
if ( ! file_exists( $filename ) )
$modified = 0;
// set 'If-Modified-Since' request header
$args += array(
'headers' => array(
'If-Modified-Since' => gmdate( DATE_RFC1123, (int)$modified ),
),
);
// fetch file and get response code & message
$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 );
$mssg = 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-geo-block' ),
'filename' => $filename,
'modified' => $modified,
);
elseif ( 200 != $code )
return array(
'code' => $code,
'message' => $code.' '.$mssg,
);
// downloaded and unzip
try {
// download file
$src = download_url( $url );
if ( is_wp_error( $src ) )
throw new Exception(
$src->get_error_code() . ' ' . $src->get_error_message()
);
// get extension
$args = strtolower( pathinfo( $url, PATHINFO_EXTENSION ) );
// unzip file
if ( 'gz' === $args && function_exists( 'gzopen' ) ) {
if ( FALSE === ( $gz = gzopen( $src, 'r' ) ) )
throw new Exception(
sprintf( __( 'Unable to read %s. Please check the permission.', 'ip-geo-block' ), $src )
);
if ( FALSE === ( $fp = @fopen( $filename, 'cb' ) ) )
throw new Exception(
sprintf( __( 'Unable to write %s. Please check the permission.', 'ip-geo-block' ), $filename )
);
if ( ! flock( $fp, LOCK_EX ) )
throw new Exception(
sprintf( __( 'Can\'t lock %s. Please try again after a while.', 'ip-geo-block' ), $filename )
);
ftruncate( $fp, 0 ); // truncate file
// same block size in wp-includes/class-http.php
while ( $data = gzread( $gz, 4096 ) ) {
fwrite( $fp, $data, strlen( $data ) );
}
}
elseif ( 'zip' === $args && class_exists( 'ZipArchive', FALSE ) ) {
$tmp = get_temp_dir(); // @since 2.5
$ret = $fs->unzip_file( $src, $tmp ); // @since 2.5
if ( is_wp_error( $ret ) )
throw new Exception(
$ret->get_error_code() . ' ' . $ret->get_error_message()
);
if ( FALSE === ( $gz = @fopen( $tmp .= basename( $filename ), 'r' ) ) )
throw new Exception(
sprintf( __( 'Unable to read %s. Please check the permission.', 'ip-geo-block' ), $tmp )
);
if ( FALSE === ( $fp = @fopen( $filename, 'cb' ) ) )
throw new Exception(
sprintf( __( 'Unable to write %s. Please check the permission.', 'ip-geo-block' ), $filename )
);
if ( ! flock( $fp, LOCK_EX ) )
throw new Exception(
sprintf( __( 'Can\'t lock %s. Please try again after a while.', 'ip-geo-block' ), $filename )
);
ftruncate( $fp, 0 ); // truncate file
// same block size in wp-includes/class-http.php
while ( $data = fread( $gz, 4096 ) ) {
fwrite( $fp, $data, strlen( $data ) );
}
}
else {
throw new Exception( __( 'gz or zip is not supported on your system.', 'ip-geo-block' ) );
}
}
// error handler
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 );
}
! empty( $gz ) and gzclose( $gz );
! empty( $tmp ) && @is_file( $tmp ) and @unlink( $tmp );
! is_wp_error( $src ) && @is_file( $src ) and @unlink( $src );
return empty( $err ) ? array(
'code' => $code,
'message' => sprintf(
__( 'Last update: %s', 'ip-geo-block' ), IP_Geo_Block_Util::localdate( $modified )
),
'filename' => $filename,
'modified' => $modified,
) : $err;
}
}
@tokkonopapa
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment