flags = $flags; if ( $this->flags & self::GEOIP_SHARED_MEMORY ) { $this->shmid = @shmop_open( self::GEOIP_SHM_KEY, "a", 0, 0 ); } else { $this->filehandle = fopen( $filename, 'rb' ) or trigger_error( "GeoIP API: Can not open $filename\n", E_USER_ERROR ); if ( $this->flags & self::GEOIP_MEMORY_CACHE ) { $s_array = fstat( $this->filehandle ); $this->memory_buffer = fread( $this->filehandle, $s_array['size'] ); } } $this->_setup_segments(); } private function _setup_segments() { $this->databaseType = self::GEOIP_COUNTRY_EDITION; $this->record_length = self::STANDARD_RECORD_LENGTH; if ($this->flags & self::GEOIP_SHARED_MEMORY) { $offset = @shmop_size($this->shmid) - 3; for ($i = 0; $i < self::STRUCTURE_INFO_MAX_SIZE; $i++) { $delim = @shmop_read($this->shmid, $offset, 3); $offset += 3; if ($delim == (chr(255) . chr(255) . chr(255))) { $this->databaseType = ord(@shmop_read($this->shmid, $offset, 1)); if ($this->databaseType >= 106) { $this->databaseType -= 105; } $offset++; if ($this->databaseType == self::GEOIP_REGION_EDITION_REV0) { $this->databaseSegments = self::GEOIP_STATE_BEGIN_REV0; } elseif ($this->databaseType == self::GEOIP_REGION_EDITION_REV1) { $this->databaseSegments = self::GEOIP_STATE_BEGIN_REV1; } elseif (($this->databaseType == self::GEOIP_CITY_EDITION_REV0) || ($this->databaseType == self::GEOIP_CITY_EDITION_REV1) || ($this->databaseType == self::GEOIP_ORG_EDITION) || ($this->databaseType == self::GEOIP_ORG_EDITION_V6) || ($this->databaseType == self::GEOIP_DOMAIN_EDITION) || ($this->databaseType == self::GEOIP_DOMAIN_EDITION_V6) || ($this->databaseType == self::GEOIP_ISP_EDITION) || ($this->databaseType == self::GEOIP_ISP_EDITION_V6) || ($this->databaseType == self::GEOIP_USERTYPE_EDITION) || ($this->databaseType == self::GEOIP_USERTYPE_EDITION_V6) || ($this->databaseType == self::GEOIP_LOCATIONA_EDITION) || ($this->databaseType == self::GEOIP_ACCURACYRADIUS_EDITION) || ($this->databaseType == self::GEOIP_CITY_EDITION_REV0_V6) || ($this->databaseType == self::GEOIP_CITY_EDITION_REV1_V6) || ($this->databaseType == self::GEOIP_NETSPEED_EDITION_REV1) || ($this->databaseType == self::GEOIP_NETSPEED_EDITION_REV1_V6) || ($this->databaseType == self::GEOIP_ASNUM_EDITION) || ($this->databaseType == self::GEOIP_ASNUM_EDITION_V6) ) { $this->databaseSegments = 0; $buf = @shmop_read($this->shmid, $offset, self::SEGMENT_RECORD_LENGTH); for ($j = 0; $j < self::SEGMENT_RECORD_LENGTH; $j++) { $this->databaseSegments += (ord($buf[$j]) << ($j * 8)); } if (($this->databaseType == self::GEOIP_ORG_EDITION) || ($this->databaseType == self::GEOIP_ORG_EDITION_V6) || ($this->databaseType == self::GEOIP_DOMAIN_EDITION) || ($this->databaseType == self::GEOIP_DOMAIN_EDITION_V6) || ($this->databaseType == self::GEOIP_ISP_EDITION) || ($this->databaseType == self::GEOIP_ISP_EDITION_V6) ) { $this->record_length = self::ORG_RECORD_LENGTH; } } break; } else { $offset -= 4; } } if (($this->databaseType == self::GEOIP_COUNTRY_EDITION) || ($this->databaseType == self::GEOIP_COUNTRY_EDITION_V6) || ($this->databaseType == self::GEOIP_PROXY_EDITION) || ($this->databaseType == self::GEOIP_NETSPEED_EDITION) ) { $this->databaseSegments = self::GEOIP_COUNTRY_BEGIN; } } else { $filepos = ftell($this->filehandle); fseek($this->filehandle, -3, SEEK_END); for ($i = 0; $i < self::STRUCTURE_INFO_MAX_SIZE; $i++) { $delim = fread($this->filehandle, 3); if ($delim == (chr(255) . chr(255) . chr(255))) { $this->databaseType = ord(fread($this->filehandle, 1)); if ($this->databaseType >= 106) { $this->databaseType -= 105; } if ($this->databaseType == self::GEOIP_REGION_EDITION_REV0) { $this->databaseSegments = self::GEOIP_STATE_BEGIN_REV0; } elseif ($this->databaseType == self::GEOIP_REGION_EDITION_REV1) { $this->databaseSegments = self::GEOIP_STATE_BEGIN_REV1; } elseif (($this->databaseType == self::GEOIP_CITY_EDITION_REV0) || ($this->databaseType == self::GEOIP_CITY_EDITION_REV1) || ($this->databaseType == self::GEOIP_CITY_EDITION_REV0_V6) || ($this->databaseType == self::GEOIP_CITY_EDITION_REV1_V6) || ($this->databaseType == self::GEOIP_ORG_EDITION) || ($this->databaseType == self::GEOIP_DOMAIN_EDITION) || ($this->databaseType == self::GEOIP_ISP_EDITION) || ($this->databaseType == self::GEOIP_ORG_EDITION_V6) || ($this->databaseType == self::GEOIP_DOMAIN_EDITION_V6) || ($this->databaseType == self::GEOIP_ISP_EDITION_V6) || ($this->databaseType == self::GEOIP_LOCATIONA_EDITION) || ($this->databaseType == self::GEOIP_ACCURACYRADIUS_EDITION) || ($this->databaseType == self::GEOIP_CITY_EDITION_REV0_V6) || ($this->databaseType == self::GEOIP_CITY_EDITION_REV1_V6) || ($this->databaseType == self::GEOIP_NETSPEED_EDITION_REV1) || ($this->databaseType == self::GEOIP_NETSPEED_EDITION_REV1_V6) || ($this->databaseType == self::GEOIP_USERTYPE_EDITION) || ($this->databaseType == self::GEOIP_USERTYPE_EDITION_V6) || ($this->databaseType == self::GEOIP_ASNUM_EDITION) || ($this->databaseType == self::GEOIP_ASNUM_EDITION_V6) ) { $this->databaseSegments = 0; $buf = fread($this->filehandle, self::SEGMENT_RECORD_LENGTH); for ($j = 0; $j < self::SEGMENT_RECORD_LENGTH; $j++) { $this->databaseSegments += (ord($buf[$j]) << ($j * 8)); } if (($this->databaseType == self::GEOIP_ORG_EDITION) || ($this->databaseType == self::GEOIP_DOMAIN_EDITION) || ($this->databaseType == self::GEOIP_ISP_EDITION) || ($this->databaseType == self::GEOIP_ORG_EDITION_V6) || ($this->databaseType == self::GEOIP_DOMAIN_EDITION_V6) || ($this->databaseType == self::GEOIP_ISP_EDITION_V6) ) { $this->record_length = self::ORG_RECORD_LENGTH; } } break; } else { fseek($this->filehandle, -4, SEEK_CUR); } } if (($this->databaseType == self::GEOIP_COUNTRY_EDITION) || ($this->databaseType == self::GEOIP_COUNTRY_EDITION_V6) || ($this->databaseType == self::GEOIP_PROXY_EDITION) || ($this->databaseType == self::GEOIP_NETSPEED_EDITION) ) { $this->databaseSegments = self::GEOIP_COUNTRY_BEGIN; } fseek($this->filehandle, $filepos, SEEK_SET); } return $this; } public function geoip_close() { if ( $this->flags & self::GEOIP_SHARED_MEMORY ) { return true; } return fclose( $this->filehandle ); } private function _common_get_record( $seek_country ) { // workaround php's broken substr, strpos, etc handling with // mbstring.func_overload and mbstring.internal_encoding $mbExists = extension_loaded('mbstring'); if ($mbExists) { $enc = mb_internal_encoding(); mb_internal_encoding('ISO-8859-1'); } $record_pointer = $seek_country + (2 * $this->record_length - 1) * $this->databaseSegments; if ($this->flags & self::GEOIP_MEMORY_CACHE) { $record_buf = substr($this->memory_buffer, $record_pointer, FULL_RECORD_LENGTH); } elseif ($this->flags & self::GEOIP_SHARED_MEMORY) { $record_buf = @shmop_read($this->shmid, $record_pointer, FULL_RECORD_LENGTH); } else { fseek($this->filehandle, $record_pointer, SEEK_SET); $record_buf = fread($this->filehandle, FULL_RECORD_LENGTH); } $record = new WC_Geo_IP_Record(); $record_buf_pos = 0; $char = ord(substr($record_buf, $record_buf_pos, 1)); $record->country_code = $this->GEOIP_COUNTRY_CODES[$char]; $record->country_code3 = $this->GEOIP_COUNTRY_CODES3[$char]; $record->country_name = $this->GEOIP_COUNTRY_NAMES[$char]; $record->continent_code = $this->GEOIP_CONTINENT_CODES[$char]; $record_buf_pos++; $str_length = 0; // Get region $char = ord(substr($record_buf, $record_buf_pos + $str_length, 1)); while ($char != 0) { $str_length++; $char = ord(substr($record_buf, $record_buf_pos + $str_length, 1)); } if ($str_length > 0) { $record->region = substr($record_buf, $record_buf_pos, $str_length); } $record_buf_pos += $str_length + 1; $str_length = 0; // Get city $char = ord(substr($record_buf, $record_buf_pos + $str_length, 1)); while ($char != 0) { $str_length++; $char = ord(substr($record_buf, $record_buf_pos + $str_length, 1)); } if ($str_length > 0) { $record->city = substr($record_buf, $record_buf_pos, $str_length); } $record_buf_pos += $str_length + 1; $str_length = 0; // Get postal code $char = ord(substr($record_buf, $record_buf_pos + $str_length, 1)); while ($char != 0) { $str_length++; $char = ord(substr($record_buf, $record_buf_pos + $str_length, 1)); } if ($str_length > 0) { $record->postal_code = substr($record_buf, $record_buf_pos, $str_length); } $record_buf_pos += $str_length + 1; $str_length = 0; // Get latitude and longitude $latitude = 0; $longitude = 0; for ($j = 0; $j < 3; ++$j) { $char = ord(substr($record_buf, $record_buf_pos++, 1)); $latitude += ($char << ($j * 8)); } $record->latitude = ($latitude / 10000) - 180; for ($j = 0; $j < 3; ++$j) { $char = ord(substr($record_buf, $record_buf_pos++, 1)); $longitude += ($char << ($j * 8)); } $record->longitude = ($longitude / 10000) - 180; if (self::GEOIP_CITY_EDITION_REV1 == $this->databaseType) { $metroarea_combo = 0; if ($record->country_code == "US") { for ($j = 0; $j < 3; ++$j) { $char = ord(substr($record_buf, $record_buf_pos++, 1)); $metroarea_combo += ($char << ($j * 8)); } $record->metro_code = $record->dma_code = floor($metroarea_combo / 1000); $record->area_code = $metroarea_combo % 1000; } } if ($mbExists) { mb_internal_encoding($enc); } return $record; } private function _get_record( $ipnum ) { $seek_country = $this->_geoip_seek_country( $ipnum ); if ( $seek_country == $this->databaseSegments ) { return null; } return $this->_common_get_record( $seek_country ); } private function _geoip_seek_country( $ipnum ) { $offset = 0; for ($depth = 31; $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); } } if ($ipnum & (1 << $depth)) { 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; } function geoip_record_by_addr( $addr ) { if ( $addr == null ) { return 0; } $ipnum = ip2long( $addr ); return $this->_get_record( $ipnum ); } 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( $addr ) { if ( $this->databaseType == self::GEOIP_CITY_EDITION_REV1 ) { $record = $this->geoip_record_by_addr( $addr); if ( $record !== false ) { return $record->country_code; } } else { $country_id = $this->geoip_country_id_by_addr( $addr ); if ( $country_id !== false ) { return $this->GEOIP_COUNTRY_CODES[ $country_id ]; } } return false; } private function _safe_substr( $string, $start, $length ) { // workaround php's broken substr, strpos, etc handling with // mbstring.func_overload and mbstring.internal_encoding $mb_exists = extension_loaded( 'mbstring' ); if ( $mb_exists ) { $enc = mb_internal_encoding(); mb_internal_encoding( 'ISO-8859-1' ); } $buf = substr( $string, $start, $length ); if ( $mb_exists ) { mb_internal_encoding( $enc ); } return $buf; } } class WC_Geo_IP_Record { public $country_code; public $country_code3; public $country_name; public $region; public $city; public $postal_code; public $latitude; public $longitude; public $area_code; public $dma_code; # metro and dma code are the same. use metro_code public $metro_code; public $continent_code; }