Added geolocate IPv6 support, closes #8184
This commit is contained in:
parent
d53d21781b
commit
44dba5036c
|
@ -1395,6 +1395,60 @@ class WC_Geo_IP {
|
|||
return $this->_common_get_record( $seek_country );
|
||||
}
|
||||
|
||||
function _geoip_seek_country_v6( $ipnum ) {
|
||||
// arrays from unpack start with offset 1
|
||||
// yet another php mystery. array_merge work around
|
||||
// this broken behaviour
|
||||
$v6vec = array_merge( unpack( 'C16', $ipnum ) );
|
||||
|
||||
$offset = 0;
|
||||
for ( $depth = 127; $depth >= 0; --$depth ) {
|
||||
if ( $this->flags & self::GEOIP_MEMORY_CACHE ) {
|
||||
$buf = $this->_safe_substr(
|
||||
$this->memory_buffer,
|
||||
2 * $this->record_length * $offset,
|
||||
2 * $this->record_length
|
||||
);
|
||||
} elseif ( $this->flags & self::GEOIP_SHARED_MEMORY ) {
|
||||
$buf = @shmop_read(
|
||||
$this->shmid,
|
||||
2 * $this->record_length * $offset,
|
||||
2 * $this->record_length
|
||||
);
|
||||
} else {
|
||||
fseek( $this->filehandle, 2 * $this->record_length * $offset, SEEK_SET ) == 0
|
||||
or trigger_error( 'GeoIP API: fseek failed', E_USER_ERROR );
|
||||
|
||||
$buf = fread( $this->filehandle, 2 * $this->record_length );
|
||||
}
|
||||
$x = array( 0, 0 );
|
||||
for ( $i = 0; $i < 2; ++$i ) {
|
||||
for ( $j = 0; $j < $this->record_length; ++$j ) {
|
||||
$x[ $i ] += ord( $buf[ $this->record_length * $i + $j ] ) << ( $j * 8 );
|
||||
}
|
||||
}
|
||||
|
||||
$bnum = 127 - $depth;
|
||||
$idx = $bnum >> 3;
|
||||
$b_mask = 1 << ( $bnum & 7 ^ 7 );
|
||||
if ( ( $v6vec[ $idx ] & $b_mask ) > 0 ) {
|
||||
if ( $x[1] >= $this->databaseSegments ) {
|
||||
return $x[1];
|
||||
}
|
||||
$offset = $x[1];
|
||||
} else {
|
||||
if ( $x[0] >= $this->databaseSegments ) {
|
||||
return $x[0];
|
||||
}
|
||||
$offset = $x[0];
|
||||
}
|
||||
}
|
||||
|
||||
trigger_error( 'GeoIP API: Error traversing database - perhaps it is corrupt?', E_USER_ERROR );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function _geoip_seek_country( $ipnum ) {
|
||||
$offset = 0;
|
||||
for ( $depth = 31; $depth >= 0; --$depth ) {
|
||||
|
@ -1412,7 +1466,7 @@ class WC_Geo_IP {
|
|||
);
|
||||
} else {
|
||||
fseek( $this->filehandle, 2 * $this->record_length * $offset, SEEK_SET ) == 0
|
||||
or trigger_error( "GeoIP API: fseek failed", E_USER_ERROR );
|
||||
or trigger_error( 'GeoIP API: fseek failed', E_USER_ERROR );
|
||||
|
||||
$buf = fread( $this->filehandle, 2 * $this->record_length );
|
||||
}
|
||||
|
@ -1438,12 +1492,12 @@ class WC_Geo_IP {
|
|||
}
|
||||
}
|
||||
|
||||
trigger_error( "GeoIP API: Error traversing database - perhaps it is corrupt?", E_USER_ERROR );
|
||||
trigger_error( 'GeoIP API: Error traversing database - perhaps it is corrupt?', E_USER_ERROR );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function geoip_record_by_addr( $addr ) {
|
||||
public function geoip_record_by_addr( $addr ) {
|
||||
if ( $addr == null ) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -1452,11 +1506,25 @@ class WC_Geo_IP {
|
|||
return $this->_get_record( $ipnum );
|
||||
}
|
||||
|
||||
function geoip_country_id_by_addr( $addr ) {
|
||||
public function geoip_country_id_by_addr_v6( $addr ) {
|
||||
$ipnum = inet_pton( $addr );
|
||||
return $this->_geoip_seek_country_v6( $ipnum ) - self::GEOIP_COUNTRY_BEGIN;
|
||||
}
|
||||
|
||||
public function geoip_country_id_by_addr( $addr ) {
|
||||
$ipnum = ip2long( $addr );
|
||||
return $this->_geoip_seek_country( $ipnum ) - self::GEOIP_COUNTRY_BEGIN;
|
||||
}
|
||||
|
||||
public function geoip_country_code_by_addr_v6( $addr ) {
|
||||
$country_id = $this->geoip_country_id_by_addr_v6( $addr );
|
||||
if ( $country_id !== false ) {
|
||||
return $this->GEOIP_COUNTRY_CODES[ $country_id ];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function geoip_country_code_by_addr( $addr ) {
|
||||
if ( $this->databaseType == self::GEOIP_CITY_EDITION_REV1 ) {
|
||||
$record = $this->geoip_record_by_addr( $addr);
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
* @author WooThemes
|
||||
* @category Admin
|
||||
* @package WooCommerce/Classes
|
||||
* @version 2.3.0
|
||||
* @version 2.4.0
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
|
@ -22,7 +22,8 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||
class WC_Geolocation {
|
||||
|
||||
/** URL to the geolocation database we're using */
|
||||
const GEOLITE_DB = 'http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz';
|
||||
const GEOLITE_DB = 'http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz';
|
||||
const GEOLITE_IPV6_DB = 'http://geolite.maxmind.com/download/geoip/database/GeoIPv6.dat.gz';
|
||||
|
||||
/** @var array API endpoints for looking up user IP address */
|
||||
private static $ip_lookup_apis = array(
|
||||
|
@ -119,12 +120,18 @@ class WC_Geolocation {
|
|||
*/
|
||||
public static function geolocate_ip( $ip_address = '', $fallback = true ) {
|
||||
// If GEOIP is enabled in CloudFlare, we can use that (Settings -> CloudFlare Settings -> Settings Overview)
|
||||
if ( ! empty( $_SERVER[ "HTTP_CF_IPCOUNTRY" ] ) ) {
|
||||
$country_code = sanitize_text_field( strtoupper( $_SERVER["HTTP_CF_IPCOUNTRY"] ) );
|
||||
if ( ! empty( $_SERVER['HTTP_CF_IPCOUNTRY'] ) ) {
|
||||
$country_code = sanitize_text_field( strtoupper( $_SERVER['HTTP_CF_IPCOUNTRY'] ) );
|
||||
} else {
|
||||
$ip_address = $ip_address ? $ip_address : self::get_ip_address();
|
||||
|
||||
if ( file_exists( self::get_local_database_path() ) ) {
|
||||
if ( self::is_IPv6( $ip_address ) ) {
|
||||
$database = self::get_local_database_path( 'v6' );
|
||||
} else {
|
||||
$database = self::get_local_database_path();
|
||||
}
|
||||
|
||||
if ( file_exists( $database ) ) {
|
||||
$country_code = self::geolocate_via_db( $ip_address );
|
||||
} else {
|
||||
$country_code = self::geolocate_via_api( $ip_address );
|
||||
|
@ -144,11 +151,14 @@ class WC_Geolocation {
|
|||
|
||||
/**
|
||||
* Path to our local db
|
||||
* @param string $version
|
||||
* @return string
|
||||
*/
|
||||
private static function get_local_database_path() {
|
||||
private static function get_local_database_path( $version = 'v4' ) {
|
||||
$version = ( 'v4' == $version ) ? '' : 'v6';
|
||||
$upload_dir = wp_upload_dir();
|
||||
return $upload_dir['basedir'] . '/GeoIP.dat';
|
||||
|
||||
return $upload_dir['basedir'] . '/GeoIP' . $version . '.dat';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -164,24 +174,29 @@ class WC_Geolocation {
|
|||
|
||||
require_once( ABSPATH . 'wp-admin/includes/file.php' );
|
||||
|
||||
$tmp_database = download_url( self::GEOLITE_DB );
|
||||
$tmp_databases = array(
|
||||
'v4' => download_url( self::GEOLITE_DB ),
|
||||
'v6' => download_url( self::GEOLITE_IPV6_DB )
|
||||
);
|
||||
|
||||
if ( ! is_wp_error( $tmp_database ) ) {
|
||||
$gzhandle = @gzopen( $tmp_database, 'r' );
|
||||
$handle = @fopen( self::get_local_database_path(), 'w' );
|
||||
foreach ( $tmp_databases as $tmp_database_version => $tmp_database_path ) {
|
||||
if ( ! is_wp_error( $tmp_database_path ) ) {
|
||||
$gzhandle = @gzopen( $tmp_database_path, 'r' );
|
||||
$handle = @fopen( self::get_local_database_path( $tmp_database_version ), 'w' );
|
||||
|
||||
if ( $gzhandle && $handle ) {
|
||||
while ( ( $string = gzread( $gzhandle, 4096 ) ) != false ) {
|
||||
fwrite( $handle, $string, strlen( $string ) );
|
||||
if ( $gzhandle && $handle ) {
|
||||
while ( ( $string = gzread( $gzhandle, 4096 ) ) != false ) {
|
||||
fwrite( $handle, $string, strlen( $string ) );
|
||||
}
|
||||
gzclose( $gzhandle );
|
||||
fclose( $handle );
|
||||
} else {
|
||||
$logger->add( 'geolocation', 'Unable to open database file' );
|
||||
}
|
||||
gzclose( $gzhandle );
|
||||
fclose( $handle );
|
||||
@unlink( $tmp_database_path );
|
||||
} else {
|
||||
$logger->add( 'geolocation', 'Unable to open database file' );
|
||||
$logger->add( 'geolocation', 'Unable to download GeoIP Database: ' . $tmp_database_path->get_error_message() );
|
||||
}
|
||||
@unlink( $tmp_database );
|
||||
} else {
|
||||
$logger->add( 'geolocation', 'Unable to download GeoIP Database: ' . $tmp_database->get_error_message() );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,11 +209,19 @@ class WC_Geolocation {
|
|||
if ( ! class_exists( 'WC_Geo_IP' ) ) {
|
||||
include_once( 'class-wc-geo-ip.php' );
|
||||
}
|
||||
$database = self::get_local_database_path();
|
||||
$gi = new WC_Geo_IP();
|
||||
|
||||
$gi->geoip_open( $database, 0 );
|
||||
$country_code = $gi->geoip_country_code_by_addr( $ip_address );
|
||||
$gi = new WC_Geo_IP();
|
||||
|
||||
if ( self::is_IPv6( $ip_address ) ) {
|
||||
$database = self::get_local_database_path( 'v6' );
|
||||
$gi->geoip_open( $database, 0 );
|
||||
$country_code = $gi->geoip_country_code_by_addr_v6( $ip_address );
|
||||
} else {
|
||||
$database = self::get_local_database_path();
|
||||
$gi->geoip_open( $database, 0 );
|
||||
$country_code = $gi->geoip_country_code_by_addr( $ip_address );
|
||||
}
|
||||
|
||||
$gi->geoip_close();
|
||||
|
||||
return sanitize_text_field( strtoupper( $country_code ) );
|
||||
|
@ -250,6 +273,18 @@ class WC_Geolocation {
|
|||
|
||||
return $country_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if is IPv6
|
||||
*
|
||||
* @since 2.4.0
|
||||
*
|
||||
* @param string $ip_address
|
||||
* @return bool
|
||||
*/
|
||||
private static function is_IPv6( $ip_address ) {
|
||||
return ! ( false === strpos( $ip_address, ':' ) );
|
||||
}
|
||||
}
|
||||
|
||||
WC_Geolocation::init();
|
||||
|
|
Loading…
Reference in New Issue