diff --git a/IP2Location/Lib/IP2Location.php b/IP2Location/Lib/IP2Location.php
new file mode 100644
index 0000000..04a2787
--- /dev/null
+++ b/IP2Location/Lib/IP2Location.php
@@ -0,0 +1,463 @@
+.
+ */
+
+class IP2LocationRecord {
+ public $ipAddress, $ipNumber, $countryCode, $countryName, $regionName, $cityName, $latitude, $longitude, $isp, $domainName, $zipCode, $timeZone, $netSpeed, $iddCode, $areaCode, $weatherStationCode, $weatherStationName, $mcc, $mnc, $mobileCarrierName, $elevation, $usageType;
+}
+
+class IP2Location {
+ // Current version
+ const VERSION = '6.0.0';
+
+ // Database storage method
+ const FILE_IO = 0;
+ const MEMORY_CACHE = 1;
+ const SHARED_MEMORY = 2;
+
+ // Unpack method
+ const ENDIAN = 0;
+ const BIG_ENDIAN = 1;
+
+ // Record field
+ const ALL = 0;
+ const COUNTRY_CODE = 1;
+ const COUNTRY_NAME = 2;
+ const REGION_NAME = 3;
+ const CITY_NAME = 4;
+ const LATITUDE = 5;
+ const LONGITUDE = 6;
+ const ISP = 7;
+ const DOMAIN_NAME = 8;
+ const ZIP_CODE = 9;
+ const TIME_ZONE = 10;
+ const NET_SPEED = 11;
+ const IDD_CODE = 12;
+ const AREA_CODE = 13;
+ const WEATHER_STATION_CODE = 14;
+ const WEATHER_STATION_NAME = 15;
+ const MCC = 16;
+ const MNC = 17;
+ const MOBILE_CARRIER_NAME = 18;
+ const ELEVATION = 19;
+ const USAGE_TYPE = 20;
+
+ // IP version
+ const IPV4 = 0;
+ const IPV6 = 1;
+
+ // SHMOP memory address
+ const SHM_KEY = 4194500608;
+
+ // Message
+ const FIELD_NOT_SUPPORTED = 'This field is not supported in DB%TYPE%. Please upgrade your IP2Location database.';
+ const INVALID_IPV4 = 'Invalid IPv4 address.';
+ const INVALID_IPV6 = 'Invalid IPv6 address.';
+
+ private $columns = array(
+ 'COUNTRY_CODE' => array(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
+ 'COUNTRY_NAME' => array(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
+ 'REGION_NAME' => array(0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
+ 'CITY_NAME' => array(0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4),
+ 'LATITUDE' => array(0, 0, 0, 0, 0, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5),
+ 'LONGITUDE' => array(0, 0, 0, 0, 0, 6, 6, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6),
+ 'ISP' => array(0, 0, 3, 0, 5, 0, 7, 5, 7, 0, 8, 0, 9, 0, 9, 0, 9, 0, 9, 7, 9, 0, 9, 7, 9),
+ 'DOMAIN_NAME' => array(0, 0, 0, 0, 0, 0, 0, 6, 8, 0, 9, 0, 10, 0, 10, 0, 10, 0, 10, 8, 10, 0, 10, 8, 10),
+ 'ZIP_CODE' => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 0, 7, 7, 7, 0, 7, 0, 7, 7, 7, 0, 7),
+ 'TIME_ZONE' => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 7, 8, 8, 8, 7, 8, 0, 8, 8, 8, 0, 8),
+ 'NET_SPEED' => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 11, 0, 11, 8, 11, 0, 11, 0, 11, 0, 11),
+ 'IDD_CODE' => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 12, 0, 12, 0, 12, 9, 12, 0, 12),
+ 'AREA_CODE' => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 13, 0, 13, 0, 13, 10, 13, 0, 13),
+ 'WEATHER_STATION_CODE' => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 14, 0, 14, 0, 14, 0, 14),
+ 'WEATHER_STATION_NAME' => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 15, 0, 15, 0, 15, 0, 15),
+ 'MCC' => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 16, 0, 16, 9, 16),
+ 'MNC' => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 17, 0, 17, 10, 17),
+ 'MOBILE_CARRIER_NAME' => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 18, 0, 18, 11, 18),
+ 'ELEVATION' => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 19, 0, 19),
+ 'USAGE_TYPE' => array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 20),
+ );
+
+ private $shmId = '';
+ private $db = array();
+ private $unpack;
+ private $buffer;
+ private $mode;
+ private $handle;
+
+ public function __construct($file=NULL, $mode=self::FILE_IO){
+ if(!is_file($file)) throw new Exception('Unable to open file "' . $file . '".');
+
+ // Define system unpack method
+ list($test) = array_values(unpack('L1L', pack('V', 1)));
+
+ // Use Big Endian Unpack if endian test failed
+ $this->unpack = (($test != 1)) ? self::BIG_ENDIAN : self::ENDIAN;
+
+ switch($mode){
+ case self::SHARED_MEMORY:
+ if(!function_exists('shmop_open')) throw new Exception('Please make sure your PHP setup has "php_shmop" enabled.');
+ $this->mode = self::SHARED_MEMORY;
+
+ $this->shmId = @shmop_open(self::SHM_KEY, 'a', 0, 0);
+
+ /*shmop_delete($this->shmId);
+ shmop_close($this->shmId);
+ die;*/
+
+ if($this->shmId === FALSE){
+ // First execution, load database into memory
+ if(($fp = fopen($file, 'rb')) === FALSE) throw new Exception('Unable to open file "' . $file . '".');
+
+ $stats = fstat($fp);
+
+ if($shmId = @shmop_open(self::SHM_KEY, 'w', 0, 0)){
+ shmop_delete($shmId);
+ shmop_close($shmId);
+ }
+
+ if($shmId = @shmop_open(self::SHM_KEY, 'c', 0644, $stats['size'])){
+ /*$buf = fread($fp, $stats['size']);
+ shmop_write($shmId, $buf, 0);*/
+
+ $offset = 0;
+ while($offset < $stats['size']){
+ $buf = fread($fp, 524288);
+ shmop_write($shmId, $buf, $offset);
+ $offset += 524288;
+ }
+ shmop_close($shmId);
+ }
+ fclose($fp);
+
+ $this->shmId = @shmop_open(self::SHM_KEY, 'a', 0, 0);
+
+ if($this->shmId === FALSE){
+ throw new Exception('Unable to access shared memory block.');
+ }
+ }
+ break;
+
+ default:
+ $this->mode = self::FILE_IO;
+ $this->handle = fopen($file, 'rb');
+
+ if($mode == self::MEMORY_CACHE){
+ $this->mode = self::MEMORY_CACHE;
+ $stats = fstat($this->handle);
+ $this->buffer = fread($this->handle, $stats['size']);
+ }
+ }
+
+ $this->db['type'] = $this->readByte(1, '8');
+ $this->db['column'] = $this->readByte(2, '8');
+ $this->db['year'] = $this->readByte(3, '8');
+ $this->db['month'] = $this->readByte(4, '8');
+ $this->db['day'] = $this->readByte(5, '8');
+ $this->db['count'] = $this->readByte(6, '32');
+ $this->db['base_address'] = $this->readByte(10, '32');
+ $this->db['ip_version'] = $this->readByte(14, '32');
+ }
+
+ private function readByte($pos, $mode='string', $autoSize = false){
+ switch($this->mode){
+ case self::SHARED_MEMORY:
+ if($mode == 'string'){
+ $data = shmop_read($this->shmId, $pos, ($autoSize) ? shmop_size($this->shmId)-$pos : 100);
+ }
+ else{
+ $data = shmop_read($this->shmId, $pos-1, 50);
+ }
+ break;
+
+ case self::MEMORY_CACHE:
+ $data = substr($this->buffer, (($mode == 'string') ? $pos : $pos-1), 100);
+ break;
+
+ default:
+ if($mode == 'string'){
+ fseek($this->handle, $pos, SEEK_SET);
+ $data = @fread($this->handle, 1);
+ }
+ else{
+ fseek($this->handle, $pos-1, SEEK_SET);
+ $data = @fread($this->handle, 50);
+ }
+ }
+
+ switch($mode){
+ case '8':
+ $out = $this->readBinary('C', $data);
+ return $out[1];
+ break;
+
+ case '32':
+ $out = $this->readBinary('V', $data);
+ if($out[1]<0) $out[1] += 4294967296;
+
+ return (int)$out[1];
+ break;
+
+ case '128':
+ $array = preg_split('//', $data, -1, PREG_SPLIT_NO_EMPTY);
+
+ if(count($array) != 16) return 0;
+
+ $ip96_127 = $this->readBinary('V', $array[0] . $array[1] . $array[2] . $array[3]);
+ $ip64_95 = $this->readBinary('V', $array[4] . $array[5] . $array[6] . $array[7]);
+ $ip32_63 = $this->readBinary('V', $array[8] . $array[9] . $array[10] . $array[11]);
+ $ip1_31 = $this->readBinary('V', $array[12] . $array[13] . $array[14] . $array[15]);
+
+ if($ip96_127[1]<0) $ip96_127[1] += 4294967296;
+ if($ip64_95[1]<0) $ip64_95[1] += 4294967296;
+ if($ip32_63[1]<0) $ip32_63[1] += 4294967296;
+ if($ip1_31[1]<0) $ip1_31[1] += 4294967296;
+
+ return bcadd(bcadd(bcmul($ip1_31[1], bcpow(4294967296, 3)), bcmul($ip32_63[1], bcpow(4294967296, 2))), bcadd( bcmul($ip64_95[1], 4294967296), $ip96_127[1]));
+ break;
+
+ case 'float':
+ $out = $this->readBinary('f', $data);
+
+ return $out[1];
+ break;
+
+ default:
+ $out = $this->readBinary('C', $data);
+ return (in_array($this->mode, array(self::SHARED_MEMORY, self::MEMORY_CACHE))) ? substr($data, 1, $out[1]) : @fread($this->handle, $out[1]);
+ }
+ }
+
+ private function readBinary($format, $data){
+ if($this->unpack == self::BIG_ENDIAN){
+ $ar = unpack($format, $data);
+ $vals = array_values($ar);
+ $f = explode('/', $format);
+ $i = 0;
+
+ foreach($f as $fKey=>$fValue){
+ $repeater = intval (substr ($fValue, 1));
+
+ if($repeater == 0) $repeater = 1;
+ if($fValue{1} == '*') $repeater = count ($ar) - $i;
+ if($fValue{0} != 'd') $i += $repeater; continue;
+
+ $j = $i + $repeater;
+
+ for($a=$i; $a<$j; ++$a){
+ $p = pack('d', $vals[$i]);
+ $p = strrev ($p);
+ list($vals[$i]) = array_values(unpack('d1d', $p));
+ ++$i;
+ }
+ }
+
+ $a = 0;
+ foreach($ar as $arKey=>$arValue){
+ $ar[$arKey] = $vals[$a];
+ ++$a;
+ }
+ return $ar;
+ }
+ return unpack($format, $data);
+ }
+
+ private function ipv6ToLong($ip){
+ $n = substr_count($ip, ':');
+
+ if($n < 7){
+ $expanded = '::';
+
+ while($n < 7){
+ $expanded .= ':';
+ $n++;
+ }
+ $ip = preg_replace('/::/', $expanded, $ip);
+ }
+
+ $subLoc = 8;
+ $ipv6No = '0';
+
+ foreach(preg_split('/:/', $ip) as $ipSub){
+ $subLoc--;
+
+ if($ipSub == '') continue;
+ $ipv6No = bcadd( $ipv6No, bcmul(hexdec($ipSub), bcpow(hexdec('0x10000'), $subLoc)));
+ }
+ return $ipv6No;
+ }
+
+ public function lookup($ip, $field=self::ALL){
+ $keys = array_keys($this->columns);
+
+ // Get record by single field name
+ if($field != self::ALL){
+ if($this->columns[$keys[$field-1]][$this->db['type']] == 0) return str_replace('%TYPE%', $this->db['type'], self::FIELD_NOT_SUPPORTED);
+ }
+
+ $result = new IP2LocationRecord();
+ $result->ipAddress = $ip;
+
+ // IPv4 database
+ if($this->db['ip_version'] == self::IPV4){
+ if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)){
+ if($field != self::ALL){
+ return self::INVALID_IPV4;
+ }
+ else{
+ $result->countryCode = $result->countryName = $result->regionName = $result->cityName = $result->latitude = $result->longitude = $result->isp = $result->domainName = $result->zipCode = $result->timeZone = $result->netSpeed = $result->iddCode = $result->areaCode = $result->weatherStationCode = $result->weatherStationName = $result->mcc = $result->mnc = $result->mobileCarrierName = $result->elevation = $result->usageType = self::INVALID_IPV4;
+ return $result;
+ }
+ }
+ $ipNumber = sprintf('%u', ip2long($ip));
+ $ipNumber = ($ipNumber >= 4294967295) ? ($ipNumber-1) : $ipNumber;
+ }
+ else{
+ if(!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)){
+ if($field != self::ALL){
+ return self::INVALID_IPV6;
+ }
+ else{
+ $result->countryCode = $result->countryName = $result->regionName = $result->cityName = $result->latitude = $result->longitude = $result->isp = $result->domainName = $result->zipCode = $result->timeZone = $result->netSpeed = $result->iddCode = $result->areaCode = $result->weatherStationCode = $result->weatherStationName = $result->mcc = $result->mnc = $result->mobileCarrierName = $result->elevation = $result->usageType = self::INVALID_IPV6;
+ return $result;
+ }
+ }
+ $ipNumber = $this->ipv6ToLong($ip);
+ $ipNumber = (bccomp($ipNumber , 340282366920938463463374607431768211455) == 0) ? bcsub($ipNumber , 1) : $ipNumber;
+ }
+
+ $result->ipNumber = $ipNumber;
+
+ $low = 0;
+ $high = $this->db['count'];
+ $mid = 0;
+ $ipFrom = 0;
+ $ipTo = 0;
+
+ while($low <= $high){
+ $mid = (int)(($low + $high)/2);
+ $ipFrom = $this->readByte($this->db['base_address'] + $mid * $this->db['column'] * 4, '32');
+ $ipTo = $this->readByte($this->db['base_address'] + ($mid + 1) * $this->db['column'] * 4, '32');
+
+ if($ipFrom < 0) $ipFrom += pow(2, 32);
+ if($ipTo < 0) $ipTo += pow(2, 32);
+
+ if(($ipNumber >= $ipFrom) && ($ipNumber < $ipTo)){
+ $offset = $this->db['base_address'] + ($mid * $this->db['column'] * 4);
+ switch($field){
+ case self::COUNTRY_CODE:
+ case self::REGION_NAME:
+ case self::CITY_NAME:
+ case self::ISP:
+ case self::DOMAIN_NAME:
+ case self::ZIP_CODE:
+ case self::TIME_ZONE:
+ case self::NET_SPEED:
+ case self::IDD_CODE:
+ case self::AREA_CODE:
+ case self::WEATHER_STATION_CODE:
+ case self::WEATHER_STATION_NAME:
+ case self::MCC:
+ case self::MNC:
+ case self::MOBILE_CARRIER_NAME:
+ case self::ELEVATION:
+ return $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[$field-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ case self::COUNTRY_NAME:
+ return $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[$field-1]][$this->db['type']]-1), '32')+3, 'string', true);
+
+ case self::LATITUDE:
+ case self::LONGITUDE:
+ return $this->readByte($offset + 4 * ($this->columns[$keys[$field-1]][$this->db['type']]-1), 'float', true);
+
+ case self::USAGE_TYPE:
+ return $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[$field-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ default:
+ $result->regionName = $result->cityName = $result->latitude = $result->longitude = $result->isp = $result->domainName = $result->zipCode = $result->timeZone = $result->netSpeed = $result->iddCode = $result->areaCode = $result->weatherStationCode = $result->weatherStationName = $result->mcc = $result->mnc = $result->mobileCarrierName = $result->elevation = $result->usageType = str_replace('%TYPE%', $this->db['type'], self::FIELD_NOT_SUPPORTED);
+
+ $result->countryCode = $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[self::COUNTRY_CODE-1]][$this->db['type']]-1), '32'), 'string', true);
+ $result->countryName = $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[self::COUNTRY_NAME-1]][$this->db['type']]-1), '32')+3, 'string', true);
+
+ if($this->columns[$keys[self::REGION_NAME-1]][$this->db['type']] != 0)
+ $result->regionName = $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[self::REGION_NAME-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ if($this->columns[$keys[self::CITY_NAME-1]][$this->db['type']] != 0)
+ $result->cityName = $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[self::CITY_NAME-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ if($this->columns[$keys[self::LATITUDE-1]][$this->db['type']] != 0)
+ $result->latitude = $this->readByte($offset + 4 * ($this->columns[$keys[self::LATITUDE-1]][$this->db['type']]-1), 'float', true);
+
+ if($this->columns[$keys[self::LONGITUDE-1]][$this->db['type']] != 0)
+ $result->longitude = $this->readByte($offset + 4 * ($this->columns[$keys[self::LONGITUDE-1]][$this->db['type']]-1), 'float', true);
+
+ if($this->columns[$keys[self::ISP-1]][$this->db['type']] != 0)$result->isp = $this->readByte($this->readByte($offset + 4 *
+ ($this->columns[$keys[self::ISP-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ if($this->columns[$keys[self::DOMAIN_NAME-1]][$this->db['type']] != 0)
+ $result->domainName = $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[self::DOMAIN_NAME-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ if($this->columns[$keys[self::ZIP_CODE-1]][$this->db['type']] != 0)
+ $result->zipCode = $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[self::ZIP_CODE-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ if($this->columns[$keys[self::TIME_ZONE-1]][$this->db['type']] != 0)
+ $result->timeZone = $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[self::TIME_ZONE-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ if($this->columns[$keys[self::NET_SPEED-1]][$this->db['type']] != 0)$result->netSpeed = $this->readByte($this->readByte($offset + 4 *
+ ($this->columns[$keys[self::NET_SPEED-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ if($this->columns[$keys[self::IDD_CODE-1]][$this->db['type']] != 0)$result->iddCode = $this->readByte($this->readByte($offset + 4 *
+ ($this->columns[$keys[self::IDD_CODE-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ if($this->columns[$keys[self::AREA_CODE-1]][$this->db['type']] != 0)
+ $result->areaCode = $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[self::AREA_CODE-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ if($this->columns[$keys[self::WEATHER_STATION_CODE-1]][$this->db['type']] != 0)
+ $result->weatherStationCode = $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[self::WEATHER_STATION_CODE-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ if($this->columns[$keys[self::WEATHER_STATION_NAME-1]][$this->db['type']] != 0)
+ $result->weatherStationName = $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[self::WEATHER_STATION_NAME-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ if($this->columns[$keys[self::MCC-1]][$this->db['type']] != 0)
+ $result->mcc = $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[self::MCC-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ if($this->columns[$keys[self::MNC-1]][$this->db['type']] != 0)
+ $result->mnc = $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[self::MNC-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ if($this->columns[$keys[self::MOBILE_CARRIER_NAME-1]][$this->db['type']] != 0)
+ $result->mobileCarrierName = $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[self::MOBILE_CARRIER_NAME-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ if($this->columns[$keys[self::ELEVATION-1]][$this->db['type']] != 0)
+ $result->elevation = $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[self::ELEVATION-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ if($this->columns[$keys[self::USAGE_TYPE-1]][$this->db['type']] != 0)
+ $result->usageType = $this->readByte($this->readByte($offset + 4 * ($this->columns[$keys[self::USAGE_TYPE-1]][$this->db['type']]-1), '32'), 'string', true);
+
+ return $result;
+ }
+ }
+ else{
+ if($ipNumber < $ipFrom){
+ $high = $mid - 1;
+ }
+ else{
+ $low = $mid + 1;
+ }
+ }
+ }
+ }
+}
+?>
\ No newline at end of file
diff --git a/IP2Location/Model/IP2LocationCore.php b/IP2Location/Model/IP2LocationCore.php
new file mode 100644
index 0000000..f61f10e
--- /dev/null
+++ b/IP2Location/Model/IP2LocationCore.php
@@ -0,0 +1,69 @@
+
+ * @copyright Copyright 2014, IP2Location.com (http://www.ip2location.com)
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ * @version 1.0
+ * @since File available since Release 2.0
+ */
+
+App::uses('AppModel', 'Model');
+
+/**
+ * Include PEAR Net_GeoIP class
+ */
+App::import('IP2Location.Lib', 'IP2Location');
+
+/**
+ * GeoIP Location class
+ */
+class IP2LocationCore extends AppModel
+{
+ /**
+ * Container for data returned by the find method
+ *
+ * @var array
+ * @access public
+ */
+ public $data = array();
+
+ /**
+ * The name of the model
+ *
+ * @var string
+ * @access public
+ */
+ public $name = 'IP2LocationCore';
+
+ public $useTable = false;
+
+ /**
+ * Find
+ *
+ * @param string $ipAddr The IP Address for which to find the location.
+ * @return mixed Array of location data or null if no location found.
+ * @access public
+ */
+ public function get($ip, $query = array())
+ {
+ $obj = new IP2Location(dirname(dirname(__FILE__)) . DS . 'data' . DS . 'IP2LOCATION.BIN', IP2Location::FILE_IO);
+
+ try {
+ $records = $obj->lookup($ip, IP2Location::ALL);
+ } catch (Exception $e) {
+ return null;
+ }
+ return $records;
+ }
+}
diff --git a/IP2Location/data/IP2LOCATION.BIN b/IP2Location/data/IP2LOCATION.BIN
new file mode 100644
index 0000000..d65f2c2
Binary files /dev/null and b/IP2Location/data/IP2LOCATION.BIN differ
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..729c84e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 IP2Location.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/README b/README
new file mode 100644
index 0000000..b1cbfbc
--- /dev/null
+++ b/README
@@ -0,0 +1,59 @@
+IP2Location CakePHP Plugin
+==========================
+IP2Location CakePHP plugin enables the user to find the country, region, city, coordinates, zip code,
+time zone, ISP, domain name, connection type, area code, weather, MCC, MNC, mobile brand name, elevation
+and usage type that any IP address or hostname originates from. It has been optimized for speed and memory
+utilization. Developers can use the API to query all IP2Location BIN databases for applications
+written using CakePHP.
+
+
+INSTALLATION
+------------
+
+1. Copy the IP2Location folder to app/Plugin.
+2. Add "CakePlugin::load('IP2Location');" to the last line of "app/Config/bootstrap.php"
+3. Download IP2Location BIN database
+ - IP2Location free LITE database at http://lite.ip2location.com
+ - IP2Location commercial database at http://www.ip2location.com
+4. Unzip and copy the BIN file into app/Plugin/IP2Location/data folder.
+5. Rename the BIN file to IP2LOCATION.BIN.
+
+Note: The plugin has included an old BIN database for your testing and development.
+You may want to download a latest copy of BIN database as the URL stated above.
+
+
+USAGE
+---------------
+
+App::uses('IP2LocationCore', 'IP2Location.Model');
+$IP2Location = new IP2LocationCore();
+$record = $IP2Location->get($this->request->clientIp());
+
+echo 'IP Address: ' . $record->ipAddress . '
';
+echo 'IP Number: ' . $record->ipNumber . '
';
+echo 'ISO Country Code: ' . $record->countryCode . '
';
+echo 'Country Name: ' . $record->countryName . '
';
+echo 'Region Name: ' . $record->regionName . '
';
+echo 'City Name: ' . $record->cityName . '
';
+echo 'Latitude: ' . $record->latitude . '
';
+echo 'Longitude: ' . $record->longitude . '
';
+echo 'ZIP Code: ' . $record->zipCode . '
';
+echo 'Time Zone: ' . $record->timeZone . '
';
+echo 'ISP Name: ' . $record->isp . '
';
+echo 'Domain Name: ' . $record->domainName . '
';
+echo 'Net Speed: ' . $record->netSpeed . '
';
+echo 'IDD Code: ' . $record->iddCode . '
';
+echo 'Area Code: ' . $record->areaCode . '
';
+echo 'Weather Station Code: ' . $record->weatherStationCode . '
';
+echo 'Weather Station Name: ' . $record->weatherStationName . '
';
+echo 'MCC: ' . $record->mcc . '
';
+echo 'MNC: ' . $record->mnc . '
';
+echo 'Mobile Carrier Name: ' . $record->mobileCarrierName . '
';
+echo 'Elevation: ' . $record->elevation . '
';
+echo 'Usage Type: ' . $record->usageType . '
';
+
+
+SUPPORT
+-------
+Email: support@ip2location.com
+Website: http://www.ip2location.com