bin_file; } $this->caching = $caching; $this->m_file = fopen($bin_file, "rb"); if (!$this->m_file) { trigger_error('Error loading '.$bin_file); if (defined('UNIT_TEST')) exit(1); return; } if ($this->caching) { $this->initCache($bin_file); } $f = $this->m_file; if ($this->caching) { $sig = $this->mem[$this->offset++] .$this->mem[$this->offset++] .$this->mem[$this->offset++] .$this->mem[$this->offset++]; } else { $sig = fread($f, 4); } if ($sig != 'ip2c') { trigger_error("file $bin_file has incorrect signature"); if (defined('UNIT_TEST')) exit(1); return; } $v = $this->readInt(); if ($v != 2) { trigger_error("file $bin_file has incorrect format version ($v)"); if (defined('UNIT_TEST')) exit(1); return; } $this->m_firstTableOffset = $this->readInt(); $this->m_numRangesFirstTable = $this->readInt(); $this->m_secondTableOffset = $this->readInt(); $this->m_numRangesSecondTable = $this->readInt(); $this->m_countriesOffset = $this->readInt(); $this->m_numCountries = $this->readInt(); $this->m_active = true; } function initCache($fileName) { $this->offset = 0; $fp = fopen($fileName, "rb"); $this->mem = fread($fp, filesize($fileName)); if ($this->mem === FALSE) $this->caching = FALSE; fclose($fp); } function get_country($ip) { if (!$this->m_active) return false; $int_ip = ip2long($ip); // happens on 64bit systems if ($int_ip > IP2C_MAX_INT) { // shift to signed int32 value $int_ip -= IP2C_MAX_INT; $int_ip -= IP2C_MAX_INT; $int_ip -= 2; } if ($int_ip >= 0) { $key = $this->find_country_code($int_ip, 0, $this->m_numRangesFirstTable, true); } else { $nip = (int)($int_ip + IP2C_MAX_INT + 2); // the + 2 is a bit wierd, but required. $key = $this->find_country_code($nip, 0, $this->m_numRangesSecondTable, false); } if ($key == false || $key == 0) { return false; } else { return $this->find_country_key($key,0, $this->m_numCountries); } } function find_country_code($ip, $startIndex, $endIndex, $firstTable, $d = 0) { while(1) { $middle = (int)(($startIndex + $endIndex) / 2); $mp = $this->getPair($middle, $firstTable); $mip = $mp['ip']; //echo "#$d find_country_code : [code=$ip, start=$startIndex, middle=$middle, end=$endIndex, mip=$mip]
"; if ($ip < $mip) { if ($startIndex + 1 == $endIndex) return false; // not found $endIndex = $middle; continue; //return $this->find_country_code($ip, $startIndex, $middle, $firstTable, ++$d); } else if ($ip > $mip) { $np = $this->getPair($middle+1, $firstTable); if ($ip < $np['ip']) { return $mp['key']; } else { if ($startIndex + 1 == $endIndex) return false; // not found $startIndex = $middle; continue; //return $this->find_country_code($ip, $middle, $endIndex, $firstTable, ++$d); } } else // ip == mip { return $mp['key']; } } } function find_country($code) { if (!$this->m_active) return false; $c = strtoupper($code); $c1 = $c[0]; $c2 = $c[1]; $key = ord($c1) * 256 + ord($c2); return $this->find_country_key($key, 0, $this->m_numCountries); } function find_country_key($code, $startIndex, $endIndex) { $d = 0; while(1) { if ($d > 20) { trigger_error("IP2Country : Internal error - endless loop detected, code = $code"); return false; } $d++; $middle = (int)(($startIndex + $endIndex) / 2); $mc = $this->get_country_code($middle); //echo "#$d find_country : [$startIndex, $endIndex, mc=$mc, code=$code]
"; if ($mc == $code) { // found. return $this->load_country($middle); } else if ($code > $mc) { if ($middle + 1 == $endIndex) { $nc = $this->get_country_code($middle); if ($nc == $code) return $this->load_country($middle); else return false; } $startIndex = $middle; continue; //return $this->find_country_key($code, $middle, $endIndex, ++$d); } else // $code < $mc { if ($startIndex + 1 == $middle) { $nc = $this->get_country_code($startIndex); if ($nc == $code) return $this->load_country($startIndex); else return false; } $endIndex = $middle; continue; //return $this->find_country_key($code, $startIndex, $middle, ++$d); } } } function load_country($index) { $offset = $this->m_countriesOffset + $index * 10; if ($this->caching) { $this->offset = $offset; } else fseek($this->m_file, $offset); $id2c = $this->readCountryKey(); $id3c = $this->read3cCode(); $nameOffset = $this->readInt(); if ($this->caching) { $this->offset = $nameOffset; } else fseek($this->m_file, $nameOffset); $len = $this->readShort(); $name = ''; if ($len != 0) { if ($this->caching) { for($i = 0;$i<$len;$i++) { $name.=$this->mem[$this->offset++]; } } else $name = fread($this->m_file, $len); } return array("id2"=>$id2c,"id3"=>$id3c,"name"=>$name); } function get_country_code($index) { $offset = $this->m_countriesOffset + $index * 10; if ($this->caching) { $this->offset = $offset; $a = unpack('n', $this->mem[$this->offset++] .$this->mem[$this->offset++]); } else { fseek($this->m_file, $offset); $a = unpack('n', fread($this->m_file, 2)); } return $a[1]; } function getPair($index, $firstTable) { $offset = 0; if ($firstTable) { if ($index > $this->m_numRangesFirstTable) { return array('key'=>false,'ip'=>0); } $offset = $this->m_firstTableOffset + $index * 6; } else { if ($index > $this->m_numRangesSecondTable) { return array('key'=>false,'ip'=>0); } $offset = $this->m_secondTableOffset + $index * 6; } if ($this->caching) { $this->offset = $offset; $p = unpack('Nip/nkey', $this->mem[$this->offset++] .$this->mem[$this->offset++] .$this->mem[$this->offset++] .$this->mem[$this->offset++] .$this->mem[$this->offset++] .$this->mem[$this->offset++]); } else { fseek($this->m_file, $offset); $p =unpack('Nip/nkey', fread($this->m_file, 6)); } return $p; } function readShort() { if ($this->caching) { $a = unpack('n', $this->mem[$this->offset++] .$this->mem[$this->offset++]); } else $a = unpack('n', fread($this->m_file, 2)); return $a[1]; } function read3cCode() { if ($this->caching) { $this->offset++; $d = $this->mem[$this->offset++] .$this->mem[$this->offset++] .$this->mem[$this->offset++]; } else { fread($this->m_file, 1); $d = fread($this->m_file, 3); } return $d != ' ' ? $d : ''; } function readCountryKey() { if ($this->caching) { return $this->mem[$this->offset++].$this->mem[$this->offset++]; } else { return fread($this->m_file, 2); } } function readInt() { if ($this->caching) { $a = unpack('N', $this->mem[$this->offset++] .$this->mem[$this->offset++] .$this->mem[$this->offset++] .$this->mem[$this->offset++]); } else $a =unpack('N', fread($this->m_file, 4)); return $a[1]; } } ?>