File: /var/www/html/site/newsite/wp-content/plugins/wp-statistics/includes/class-wp-statistics-geoip.php
<?php
namespace WP_STATISTICS;
use GeoIp2\Exception\AddressNotFoundException;
class GeoIP
{
/**
* List Geo ip Library
*
* @var array
*/
public static $library = array(
'country' => array(
'source' => 'https://cdn.jsdelivr.net/npm/geolite2-country@1.0.2/GeoLite2-Country.mmdb.gz',
'file' => 'GeoLite2-Country',
'opt' => 'geoip',
'cache' => 31536000 //1 Year
),
'city' => array(
'source' => 'https://cdn.jsdelivr.net/npm/geolite2-city@1.0.0/GeoLite2-City.mmdb.gz',
'file' => 'GeoLite2-City',
'opt' => 'geoip_city',
'cache' => 6998000 //3 Month
)
);
/**
* Geo IP file Extension
*
* @var string
*/
public static $file_extension = 'mmdb';
/**
* Default Private Country
*
* @var String
*/
public static $private_country = '000';
/**
* Cache Option name For Store User City
*
* @var string
*/
public static $city_cache_object_name = 'wp_statistics_users_city';
/**
* Get Geo IP Path
*
* @param $pack
* @return mixed
*/
public static function get_geo_ip_path($pack)
{
return wp_normalize_path(path_join(Helper::get_uploads_dir(WP_STATISTICS_UPLOADS_DIR), self::$library[strtolower($pack)]['file'] . '.' . self::$file_extension));
}
/**
* Check Is Active Geo-ip
*
* @param bool $which
* @param bool $CheckDBFile
* @return boolean
*/
public static function active($which = false, $CheckDBFile = true)
{
//Default Geo-Ip Option name
$which = ($which === false ? 'country' : $which);
$opt = ($which == "city" ? 'geoip_city' : 'geoip');
$value = Option::get($opt);
//Check Exist GEO-IP file
$file = self::get_geo_ip_path($which);
if ($CheckDBFile and !file_exists($file)) {
if ($value) {
Option::update($opt, false);
}
return false;
}
// Return
return $value;
}
/**
* geo ip Loader
*
* @param $pack
* @return bool|\GeoIp2\Database\Reader
*/
public static function Loader($pack)
{
// Check file Exist
$file = self::get_geo_ip_path($pack);
if (file_exists($file)) {
try {
//Load GeoIP Reader
return new \GeoIp2\Database\Reader($file);
} catch (\Exception $e) {
\WP_Statistics::log($e->getMessage());
return false;
}
} else {
return false;
}
}
/**
* Get Default Country Code
*
* @return String
*/
public static function getDefaultCountryCode()
{
$opt = Option::get('private_country_code');
if (isset($opt) and !empty($opt)) {
return trim($opt);
}
return self::$private_country;
}
/**
* Get Country Detail By User IP
*
* @param bool $ip
* @param string $return
* @return String|null
* @throws \Exception
* @see https://github.com/maxmind/GeoIP2-php
*/
public static function getCountry($ip = false, $return = 'isoCode')
{
// Check in WordPress Cache
$user_country = wp_cache_get('country-' . $ip, 'wp-statistics');
if ($user_country != false) {
return $user_country;
}
// Check in WordPress Database
$user_country = self::getUserCountryFromDB($ip);
if ($user_country != false) {
wp_cache_set('country-' . $ip, $user_country, 'wp-statistics', DAY_IN_SECONDS);
return $user_country;
}
// Default Country Name
$default_country = self::getDefaultCountryCode();
// Get User IP
$ip = ($ip === false ? IP::getIP() : $ip);
// Check Unknown IP
if ($default_country != self::$private_country) {
if (IP::CheckIPRange(IP::$private_SubNets)) {
return $default_country;
}
}
// Sanitize IP
if (IP::isIP($ip) === false) {
return $default_country;
}
// Load GEO-IP
$reader = self::Loader('country');
//Get Country name
if ($reader != false) {
try {
//Search in Geo-IP
$record = $reader->country($ip);
//Get Country
if ($return == "all") {
$location = $record->country;
} else {
$location = $record->country->{$return};
}
} catch (\Exception $e) {
\WP_Statistics::log($e->getMessage());
}
}
# Check Has Location
if (isset($location) and !empty($location)) {
wp_cache_set('country-' . $ip, $location, 'wp-statistics', DAY_IN_SECONDS);
return $location;
}
return $default_country;
}
/**
* Get User Country From Database
*
* @param $ip
* @return false|string
*/
public static function getUserCountryFromDB($ip)
{
global $wpdb;
$date = date('Y-m-d', current_time('timestamp') - self::$library['country']['cache']);
$user = $wpdb->get_row("SELECT `location` FROM " . DB::table('visitor') . " WHERE `ip` = '{$ip}' and `last_counter` >= '{$date}' ORDER BY `ID` DESC LIMIT 1");
if (null !== $user) {
return $user->location;
}
return false;
}
/**
* This function downloads the GeoIP database from MaxMind.
*
* @param $pack
* @param string $type
*
* @return mixed
*/
public static function download($pack, $type = "enable")
{
// Create Empty Return Function
$result["status"] = false;
// Sanitize Pack name
$pack = strtolower($pack);
// If GeoIP is disabled, bail out.
if ($type == "update" and Option::get(GeoIP::$library[$pack]['opt']) == '') {
return '';
}
// Load Require Function
if (!function_exists('download_url')) {
include(ABSPATH . 'wp-admin/includes/file.php');
}
if (!function_exists('wp_generate_password')) {
include(ABSPATH . 'wp-includes/pluggable.php');
}
// Get the upload directory from WordPress.
$upload_dir = wp_upload_dir();
// We need the gzopen() function
if (false === function_exists('gzopen')) {
if ($type == "enable") {
Option::update(GeoIP::$library[$pack]['opt'], '');
}
return array_merge($result, array("notice" => __('Error the gzopen() function do not exist!', 'wp-statistics')));
}
// This is the location of the file to download.
$download_url = GeoIP::$library[$pack]['source'];
$response = wp_remote_get($download_url, array(
'timeout' => 60,
'sslverify' => false
));
if (is_wp_error($response)) {
\WP_Statistics::log(array('code' => 'download_geoip', 'type' => $pack, 'message' => $response->get_error_message()));
return array_merge($result, array("notice" => $response->get_error_message()));
}
// Change download url if the maxmind.com doesn't response.
if (wp_remote_retrieve_response_code($response) != '200') {
return array_merge($result, array("notice" => sprintf(__('Error to get %s from %s', 'wp-statistics'), $pack, $download_url)));
}
// Create a variable with the name of the database file to download.
$DBFile = self::get_geo_ip_path($pack);
// Check to see if the subdirectory we're going to upload to exists, if not create it.
if (!file_exists($upload_dir['basedir'] . '/' . WP_STATISTICS_UPLOADS_DIR)) {
if (!@mkdir($upload_dir['basedir'] . '/' . WP_STATISTICS_UPLOADS_DIR, 0755)) {
if ($type == "enable") {
Option::update(GeoIP::$library[$pack]['opt'], '');
}
return array_merge($result, array("notice" => sprintf(__('Error creating GeoIP database directory, make sure your web server has permissions to create directories in: %s', 'wp-statistics'), $upload_dir['basedir'])));
}
}
if (!is_writable($upload_dir['basedir'] . '/' . WP_STATISTICS_UPLOADS_DIR)) {
if ($type == "enable") {
Option::update(GeoIP::$library[$pack]['opt'], '');
}
return array_merge($result, array("notice" => sprintf(__('Error setting permissions of the GeoIP database directory, make sure your web server has permissions to write to directories in : %s', 'wp-statistics'), $upload_dir['basedir'])));
}
// Download the file from MaxMind, this places it in a temporary location.
$TempFile = download_url($download_url);
// If we failed, through a message, otherwise proceed.
if (is_wp_error($TempFile)) {
if ($type == "enable") {
Option::update(GeoIP::$library[$pack]['opt'], '');
}
return array_merge($result, array("notice" => sprintf(__('Error downloading GeoIP database from: %s - %s', 'wp-statistics'), $download_url, $TempFile->get_error_message())));
} else {
// Open the downloaded file to unzip it.
$ZipHandle = gzopen($TempFile, 'rb');
// Create th new file to unzip to.
$DBfh = fopen($DBFile, 'wb');
// If we failed to open the downloaded file, through an error and remove the temporary file. Otherwise do the actual unzip.
if (!$ZipHandle) {
if ($type == "enable") {
Option::update(GeoIP::$library[$pack]['opt'], '');
}
unlink($TempFile);
return array_merge($result, array("notice" => sprintf(__('Error could not open downloaded GeoIP database for reading: %s', 'wp-statistics'), $TempFile)));
} else {
// If we failed to open the new file, throw and error and remove the temporary file. Otherwise actually do the unzip.
if (!$DBfh) {
if ($type == "enable") {
Option::update(GeoIP::$library[$pack]['opt'], '');
}
unlink($TempFile);
return array_merge($result, array("notice" => sprintf(__('Error could not open destination GeoIP database for writing %s', 'wp-statistics'), $DBFile)));
} else {
while (($data = gzread($ZipHandle, 4096)) != false) {
fwrite($DBfh, $data);
}
// Close the files.
gzclose($ZipHandle);
fclose($DBfh);
// Delete the temporary file.
unlink($TempFile);
// Display the success message.
$result["status"] = true;
$result["notice"] = __('GeoIP Database updated successfully!', 'wp-statistics');
// Update the options to reflect the new download.
if ($type == "update") {
Option::update('last_geoip_dl', time());
Option::update('update_geoip', false);
}
// Populate any missing GeoIP information if the user has selected the option.
if ($pack == "country") {
if (Option::get('geoip') && GeoIP::IsSupport() && Option::get('auto_pop')) {
self::Update_GeoIP_Visitor();
}
}
}
}
}
// Send Email
if (Option::get('geoip_report') == true) {
Helper::send_mail(Option::getEmailNotification(), __('GeoIP update on', 'wp-statistics') . ' ' . get_bloginfo('name'), $result['notice']);
}
return $result;
}
/**
* Update All GEO-IP Visitors
*
* @return array
*/
public static function Update_GeoIP_Visitor()
{
global $wpdb;
// Find all rows in the table that currently don't have GeoIP info or have an unknown ('000') location.
$result = $wpdb->get_results("SELECT id,ip FROM `" . DB::table('visitor') . "` WHERE location = '' or location = '" . GeoIP::$private_country . "' or location IS NULL");
// Try create a new reader instance.
$reader = false;
if (Option::get('geoip')) {
$reader = GeoIP::Loader('country');
}
if ($reader === false) {
return array('status' => false, 'data' => __('Unable to load the GeoIP database, make sure you have downloaded it in the settings page.', 'wp-statistics'));
}
$count = 0;
// Loop through all the missing rows and update them if we find a location for them.
foreach ($result as $item) {
$count++;
// If the IP address is only a hash, don't bother updating the record.
if (IP::IsHashIP($item->ip) === false and $reader != false) {
try {
$record = $reader->country($item->ip);
$location = $record->country->isoCode;
if ($location == "") {
$location = GeoIP::$private_country;
}
} catch (\Exception $e) {
\WP_Statistics::log($e->getMessage());
$location = GeoIP::$private_country;
}
// Update the row in the database.
$wpdb->update(
DB::table('visitor'),
array('location' => $location),
array('id' => $item->id)
);
}
}
return array('status' => true, 'data' => sprintf(__('Updated %s GeoIP records in the visitors database.', 'wp-statistics'), $count));
}
/**
* if PHP modules we need for GeoIP exists.
*
* @return bool
*/
public static function IsSupport()
{
$enabled = true;
// PHP cURL extension installed
if (!function_exists('curl_init')) {
$enabled = false;
}
// PHP NOT running in safe mode
if (ini_get('safe_mode')) {
// Double check php version, 5.4 and above don't support safe mode but the ini value may still be set after an upgrade.
if (!version_compare(phpversion(), '5.4', '<')) {
$enabled = false;
}
}
return $enabled;
}
/**
* Get City Detail By User IP
*
* @param bool $ip
* @param string $return
* @return String|null
* @throws \Exception
* @see https://github.com/maxmind/GeoIP2-php
*/
public static function getCity($ip = false, $return = 'name')
{
// Check in WordPress WP_Cache
$user_city = wp_cache_get('city-' . $ip, 'wp-statistics');
if ($user_city != false) {
return $user_city;
}
// Check in WordPress Persist Cache
$user_city = self::getCacheCity($ip);
if ($user_city != false) {
self::setCacheCity($ip, $user_city);
return $user_city;
}
// Default City Name
$default_city = __('Unknown', 'wp-statistics');
// Get User IP
$ip = ($ip === false ? IP::getIP() : $ip);
// Sanitize IP
if (IP::isIP($ip) === false) {
return $default_city;
}
// Load GEO-IP
$reader = self::Loader('city');
//Get City name
if ($reader != false) {
try {
//Search in Geo-IP
$record = $reader->city($ip);
//Get City
if ($return == "all") {
$location = $record->city;
} elseif ($return == "name") {
$subdiv = $record->mostSpecificSubdivision->name;
$city = $record->city->name;
$location = ($city ? $city : "(Unknown)") . ($subdiv ? (", ") : "") . $subdiv;
} else {
$location = $record->city->{$return};
}
} catch (\Exception $e) {
/**
* For debugging, you can comment out the logger.
*/
//\WP_Statistics::log($e->getMessage());
}
}
# Check Has Location
if (isset($location) and !empty($location)) {
self::setCacheCity($ip, $location);
return $location;
}
return $default_city;
}
/**
* Set Cache User City
*
* @param $ip
* @param $city
* @param bool $opt
*/
public static function setCacheCity($ip, $city, $opt = false)
{
if (!$opt) {
$opt = get_option(self::$city_cache_object_name);
}
if (empty($opt) || !is_array($opt)) {
$opt = array();
}
$opt[$ip] = array($city, current_time('timestamp'));
wp_cache_set('city-' . $ip, $city, 'wp-statistics', DAY_IN_SECONDS);
$opt = self::cleanCacheCity($opt);
update_option(self::$city_cache_object_name, $opt, 'no');
}
/**
* Clean Cache City
*
* @param bool $option
* @param bool $save
* @return bool
*/
public static function cleanCacheCity($option = false, $save = false)
{
if (!$option) {
$option = get_option(self::$city_cache_object_name);
}
if (!empty($option) and is_array($option) and count($option) > 0) {
foreach ($option as $ip => $value) {
if (isset($value[1])) {
$expire_time = (int)$value[1] + self::$library['city']['cache'];
if ($expire_time <= current_time('timestamp')) {
unset($option[$ip]);
}
}
}
}
if ($save) {
update_option(self::$city_cache_object_name, $option, 'no');
}
return $option;
}
/*
* Get City with IP From WordPress Option Cache
*/
public static function getCacheCity($ip)
{
$opt = get_option(self::$city_cache_object_name);
return (isset($opt[$ip]) ? $opt[$ip][0] : false);
}
/**
* Geo IP Tools Link
*
* @param $ip
* @return string
*/
public static function geoIPTools($ip)
{
//return "http://www.geoiptool.com/en/?IP={$ip}";
return "https://redirect.li/map/?ip={$ip}";
}
}