/**
  * Ajax callback function
  *
  * @link http://codex.wordpress.org/AJAX_in_Plugins
  * @link http://codex.wordpress.org/Function_Reference/check_ajax_referer
  * @link http://core.trac.wordpress.org/browser/trunk/wp-admin/admin-ajax.php
  */
 public function admin_ajax_callback()
 {
     // Check request origin, nonce, capability.
     if (!check_admin_referer($this->get_ajax_action(), 'nonce') || !current_user_can('manage_options') || empty($_POST)) {
         // @since 2.0
         status_header(403);
         // Forbidden @since 2.0.0
     }
     $which = isset($_POST['which']) ? $_POST['which'] : NULL;
     switch (isset($_POST['cmd']) ? $_POST['cmd'] : NULL) {
         case 'download':
             $res = IP_Geo_Block::download_database();
             break;
         case 'search':
             require_once IP_GEO_BLOCK_PATH . 'classes/class-ip-geo-block-apis.php';
             // check format
             if (filter_var($ip = $_POST['ip'], FILTER_VALIDATE_IP)) {
                 // get option settings and compose request headers
                 $options = IP_Geo_Block::get_option('settings');
                 $args = IP_Geo_Block::get_request_headers($options);
                 // create object for provider and get location
                 if ($geo = IP_Geo_Block_API::get_instance($which, $options)) {
                     $res = $geo->get_location($ip, $args);
                 } else {
                     $res = array('errorMessage' => 'Unknown service.');
                 }
             } else {
                 $res = array('errorMessage' => 'Invalid IP address.');
             }
             break;
         case 'scan-code':
             require_once IP_GEO_BLOCK_PATH . 'classes/class-ip-geo-block-apis.php';
             // scan all the country code using selected APIs
             $ip = IP_Geo_Block::get_ip_address();
             $options = IP_Geo_Block::get_option('settings');
             $args = IP_Geo_Block::get_request_headers($options);
             $type = IP_Geo_Block_Provider::get_providers('type', FALSE, FALSE);
             $providers = IP_Geo_Block_Provider::get_valid_providers($options['providers'], FALSE, FALSE);
             $res['IP address'] = esc_html($ip);
             foreach ($providers as $provider) {
                 if ($geo = IP_Geo_Block_API::get_instance($provider, $options)) {
                     $ret = $geo->get_location($ip, $args);
                     $res[$provider] = array('type' => $type[$provider], 'code' => esc_html(FALSE === $ret ? __('n/a', IP_Geo_Block::TEXT_DOMAIN) : (!empty($ret['errorMessage']) ? $ret['errorMessage'] : (!empty($ret['countryCode']) ? $ret['countryCode'] : __('UNKNOWN', IP_Geo_Block::TEXT_DOMAIN)))));
                 }
             }
             break;
         case 'clear-statistics':
             // set default values
             update_option($this->option_name['statistics'], IP_Geo_Block::get_default('statistics'));
             $res = array('page' => 'options-general.php?page=' . IP_Geo_Block::PLUGIN_SLUG, 'tab' => 'tab=1');
             break;
         case 'clear-cache':
             // delete cache of IP address
             delete_transient(IP_Geo_Block::CACHE_KEY);
             // @since 2.8
             $res = array('page' => 'options-general.php?page=' . IP_Geo_Block::PLUGIN_SLUG, 'tab' => 'tab=1');
             break;
         case 'clear-logs':
             require_once IP_GEO_BLOCK_PATH . 'classes/class-ip-geo-block-logs.php';
             $hook = array('comment', 'login', 'admin', 'xmlrpc');
             $which = in_array($which, $hook) ? $which : NULL;
             IP_Geo_Block_Logs::clean_log($which);
             $res = array('page' => 'options-general.php?page=' . IP_Geo_Block::PLUGIN_SLUG, 'tab' => 'tab=4');
             break;
         case 'restore':
             require_once IP_GEO_BLOCK_PATH . 'includes/localdate.php';
             require_once IP_GEO_BLOCK_PATH . 'classes/class-ip-geo-block-logs.php';
             // if js is slow then limit the number of rows
             $limit = IP_Geo_Block_Logs::limit_rows(@$_POST['time']);
             // compose html with sanitization
             $which = IP_Geo_Block_Logs::restore_log($which);
             foreach ($which as $hook => $rows) {
                 $html = '';
                 $n = 0;
                 foreach ($rows as $logs) {
                     $log = (int) array_shift($logs);
                     $html .= "<tr><td data-value={$log}>";
                     $html .= ip_geo_block_localdate($log, 'Y-m-d H:i:s') . "</td>";
                     foreach ($logs as $log) {
                         $log = esc_html($log);
                         $html .= "<td>{$log}</td>";
                     }
                     $html .= "</tr>";
                     if (++$n >= $limit) {
                         break;
                     }
                 }
                 $res[$hook] = $html;
             }
             break;
         case 'create_table':
         case 'delete_table':
             require_once IP_GEO_BLOCK_PATH . 'classes/class-ip-geo-block-logs.php';
             if ('create_table' === $_POST['cmd']) {
                 IP_Geo_Block_Logs::create_log();
             } else {
                 IP_Geo_Block_Logs::delete_log();
             }
             $res = array('page' => 'options-general.php?page=' . IP_Geo_Block::PLUGIN_SLUG);
     }
     if (isset($res)) {
         // wp_send_json_{success,error}() @since 3.5.0
         wp_send_json($res);
     }
     // @since 3.5.0
     die;
     // End of ajax
 }
 /**
  * Validate ip address.
  *
  * @param string $hook a name to identify action hook applied in this call.
  * @param array $settings option settings
  * @param boolean $die send http response and die if validation fails
  */
 private function validate_ip($hook, $settings, $block = TRUE, $die = TRUE)
 {
     // set IP address to be validated
     $ips = array(self::get_ip_address());
     // pick up all the IPs in HTTP_X_FORWARDED_FOR, HTTP_CLIENT_IP and etc.
     foreach (explode(',', $settings['validation']['proxy']) as $var) {
         if (isset($_SERVER[$var])) {
             foreach (explode(',', $_SERVER[$var]) as $ip) {
                 if (!in_array($ip = trim($ip), $ips) && filter_var($ip, FILTER_VALIDATE_IP)) {
                     array_unshift($ips, $ip);
                 }
             }
         }
     }
     // register auxiliary validation functions
     $var = self::PLUGIN_SLUG . "-{$hook}";
     add_filter($var, array($this, 'check_auth'), 9, 2);
     add_filter($var, array($this, 'check_fail'), 8, 2);
     $settings['extra_ips'] = apply_filters(self::PLUGIN_SLUG . '-extra-ips', $settings['extra_ips'], $hook);
     $settings['extra_ips']['white_list'] && add_filter($var, array($this, 'check_ips_white'), 7, 2);
     $settings['extra_ips']['black_list'] && add_filter($var, array($this, 'check_ips_black'), 7, 2);
     // make valid provider name list
     require_once IP_GEO_BLOCK_PATH . 'classes/class-ip-geo-block-apis.php';
     $providers = IP_Geo_Block_Provider::get_valid_providers($settings['providers']);
     // apply custom filter for validation
     // @usage add_filter( "ip-geo-block-$hook", 'my_validation' );
     // @param $validate = array(
     //     'ip'       => $ip,       /* validated ip address                */
     //     'auth'     => $auth,     /* authenticated or not                */
     //     'code'     => $code,     /* country code or reason of rejection */
     //     'result'   => $result,   /* 'passed', 'blocked'                 */
     // );
     foreach ($ips as $this->remote_addr) {
         $validate = self::_get_geolocation($this->remote_addr, $settings, $providers);
         $validate = apply_filters($var, $validate, $settings);
         // if no 'result' then validate ip address by country
         if (empty($validate['result'])) {
             $validate = self::validate_country($validate, $settings, $block);
         }
         // if one of IPs is blocked then stop
         if ($result = 'passed' !== $validate['result']) {
             break;
         }
     }
     // update cache
     IP_Geo_Block_API_Cache::update_cache($hook, $validate, $settings);
     if ($die) {
         // record log (0:no, 1:blocked, 2:passed, 3:unauth, 4:auth, 5:all)
         $var = (int) $settings['validation']['reclogs'];
         if (1 === $var && $result || 2 === $var && !$result || 3 === $var && !$validate['auth'] || 4 === $var && $validate['auth'] || 5 === $var) {
             // all
             require_once IP_GEO_BLOCK_PATH . 'classes/class-ip-geo-block-logs.php';
             IP_Geo_Block_Logs::record_log($hook, $validate, $settings);
         }
         if ($result) {
             // update statistics
             if ($settings['save_statistics']) {
                 $this->update_statistics($hook, $validate);
             }
             // send response code to refuse
             $this->send_response($hook, $settings['response_code']);
         }
     }
     return $validate;
 }