1<?php
2
3/* geoipcity.inc
4 *
5 * Copyright (C) 2013 MaxMind, Inc.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307    USA
20 */
21
22
23define("FULL_RECORD_LENGTH", 50);
24
25require_once 'geoip.inc';
26require_once 'geoipregionvars.php';
27
28class geoiprecord
29{
30    public $country_code;
31    public $country_code3;
32    public $country_name;
33    public $region;
34    public $city;
35    public $postal_code;
36    public $latitude;
37    public $longitude;
38    public $area_code;
39    public $dma_code; # metro and dma code are the same. use metro_code
40    public $metro_code;
41    public $continent_code;
42}
43
44function _get_record_v6($gi, $ipnum)
45{
46    $seek_country = _geoip_seek_country_v6($gi, $ipnum);
47    if ($seek_country == $gi->databaseSegments) {
48        return null;
49    }
50    return _common_get_record($gi, $seek_country);
51}
52
53function _common_get_record($gi, $seek_country)
54{
55    // workaround php's broken substr, strpos, etc handling with
56    // mbstring.func_overload and mbstring.internal_encoding
57    $mbExists = extension_loaded('mbstring');
58    if ($mbExists) {
59        $enc = mb_internal_encoding();
60        mb_internal_encoding('ISO-8859-1');
61    }
62
63    $record_pointer = $seek_country + (2 * $gi->record_length - 1) * $gi->databaseSegments;
64
65    if ($gi->flags & GEOIP_MEMORY_CACHE) {
66        $record_buf = substr($gi->memory_buffer, $record_pointer, FULL_RECORD_LENGTH);
67    } elseif ($gi->flags & GEOIP_SHARED_MEMORY) {
68        $record_buf = @shmop_read($gi->shmid, $record_pointer, FULL_RECORD_LENGTH);
69    } else {
70        fseek($gi->filehandle, $record_pointer, SEEK_SET);
71        $record_buf = fread($gi->filehandle, FULL_RECORD_LENGTH);
72    }
73    $record = new geoiprecord;
74    $record_buf_pos = 0;
75    $char = ord(substr($record_buf, $record_buf_pos, 1));
76    $record->country_code = $gi->GEOIP_COUNTRY_CODES[$char];
77    $record->country_code3 = $gi->GEOIP_COUNTRY_CODES3[$char];
78    $record->country_name = $gi->GEOIP_COUNTRY_NAMES[$char];
79    $record->continent_code = $gi->GEOIP_CONTINENT_CODES[$char];
80    $record_buf_pos++;
81    $str_length = 0;
82
83    // Get region
84    $char = ord(substr($record_buf, $record_buf_pos + $str_length, 1));
85    while ($char != 0) {
86        $str_length++;
87        $char = ord(substr($record_buf, $record_buf_pos + $str_length, 1));
88    }
89    if ($str_length > 0) {
90        $record->region = substr($record_buf, $record_buf_pos, $str_length);
91    }
92    $record_buf_pos += $str_length + 1;
93    $str_length = 0;
94    // Get city
95    $char = ord(substr($record_buf, $record_buf_pos + $str_length, 1));
96    while ($char != 0) {
97        $str_length++;
98        $char = ord(substr($record_buf, $record_buf_pos + $str_length, 1));
99    }
100    if ($str_length > 0) {
101        $record->city = substr($record_buf, $record_buf_pos, $str_length);
102    }
103    $record_buf_pos += $str_length + 1;
104    $str_length = 0;
105    // Get postal code
106    $char = ord(substr($record_buf, $record_buf_pos + $str_length, 1));
107    while ($char != 0) {
108        $str_length++;
109        $char = ord(substr($record_buf, $record_buf_pos + $str_length, 1));
110    }
111    if ($str_length > 0) {
112        $record->postal_code = substr($record_buf, $record_buf_pos, $str_length);
113    }
114    $record_buf_pos += $str_length + 1;
115    $str_length = 0;
116    // Get latitude and longitude
117    $latitude = 0;
118    $longitude = 0;
119    for ($j = 0; $j < 3; ++$j) {
120        $char = ord(substr($record_buf, $record_buf_pos++, 1));
121        $latitude += ($char << ($j * 8));
122    }
123    $record->latitude = ($latitude / 10000) - 180;
124    for ($j = 0; $j < 3; ++$j) {
125        $char = ord(substr($record_buf, $record_buf_pos++, 1));
126        $longitude += ($char << ($j * 8));
127    }
128    $record->longitude = ($longitude / 10000) - 180;
129    if (GEOIP_CITY_EDITION_REV1 == $gi->databaseType) {
130        $metroarea_combo = 0;
131        if ($record->country_code == "US") {
132            for ($j = 0; $j < 3; ++$j) {
133                $char = ord(substr($record_buf, $record_buf_pos++, 1));
134                $metroarea_combo += ($char << ($j * 8));
135            }
136            $record->metro_code = $record->dma_code = floor($metroarea_combo / 1000);
137            $record->area_code = $metroarea_combo % 1000;
138        }
139    }
140    if ($mbExists) {
141        mb_internal_encoding($enc);
142    }
143    return $record;
144}
145
146function GeoIP_record_by_addr_v6($gi, $addr)
147{
148    if ($addr == null) {
149        return 0;
150    }
151    $ipnum = inet_pton($addr);
152    return _get_record_v6($gi, $ipnum);
153}
154
155function _get_record($gi, $ipnum)
156{
157    $seek_country = _geoip_seek_country($gi, $ipnum);
158    if ($seek_country == $gi->databaseSegments) {
159        return null;
160    }
161    return _common_get_record($gi, $seek_country);
162}
163
164function GeoIP_record_by_addr($gi, $addr)
165{
166    if ($addr == null) {
167        return 0;
168    }
169    $ipnum = ip2long($addr);
170    return _get_record($gi, $ipnum);
171}
172