1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
26: class Horde_Nls_Geoip
27: {
28:
29: const GEOIP_COUNTRY_BEGIN = 16776960;
30: const STRUCTURE_INFO_MAX_SIZE = 20;
31: const STANDARD_RECORD_LENGTH = 3;
32:
33: 34: 35: 36: 37:
38: protected $_countryCodes = array(
39: '', 'AP', 'EU', 'AD', 'AE', 'AF', 'AG', 'AI', 'AL', 'AM', 'AN', 'AO',
40: 'AQ', 'AR', 'AS', 'AT', 'AU', 'AW', 'AZ', 'BA', 'BB', 'BD', 'BE',
41: 'BF', 'BG', 'BH', 'BI', 'BJ', 'BM', 'BN', 'BO', 'BR', 'BS', 'BT',
42: 'BV', 'BW', 'BY', 'BZ', 'CA', 'CC', 'CD', 'CF', 'CG', 'CH', 'CI',
43: 'CK', 'CL', 'CM', 'CN', 'CO', 'CR', 'CU', 'CV', 'CX', 'CY', 'CZ',
44: 'DE', 'DJ', 'DK', 'DM', 'DO', 'DZ', 'EC', 'EE', 'EG', 'EH', 'ER',
45: 'ES', 'ET', 'FI', 'FJ', 'FK', 'FM', 'FO', 'FR', 'FX', 'GA', 'UK',
46: 'GD', 'GE', 'GF', 'GH', 'GI', 'GL', 'GM', 'GN', 'GP', 'GQ', 'GR',
47: 'GS', 'GT', 'GU', 'GW', 'GY', 'HK', 'HM', 'HN', 'HR', 'HT', 'HU',
48: 'ID', 'IE', 'IL', 'IN', 'IO', 'IQ', 'IR', 'IS', 'IT', 'JM', 'JO',
49: 'JP', 'KE', 'KG', 'KH', 'KI', 'KM', 'KN', 'KP', 'KR', 'KW', 'KY',
50: 'KZ', 'LA', 'LB', 'LC', 'LI', 'LK', 'LR', 'LS', 'LT', 'LU', 'LV',
51: 'LY', 'MA', 'MC', 'MD', 'MG', 'MH', 'MK', 'ML', 'MM', 'MN', 'MO',
52: 'MP', 'MQ', 'MR', 'MS', 'MT', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ',
53: 'NA', 'NC', 'NE', 'NF', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NU',
54: 'NZ', 'OM', 'PA', 'PE', 'PF', 'PG', 'PH', 'PK', 'PL', 'PM', 'PN',
55: 'PR', 'PS', 'PT', 'PW', 'PY', 'QA', 'RE', 'RO', 'RU', 'RW', 'SA',
56: 'SB', 'SC', 'SD', 'SE', 'SG', 'SH', 'SI', 'SJ', 'SK', 'SL', 'SM',
57: 'SN', 'SO', 'SR', 'ST', 'SV', 'SY', 'SZ', 'TC', 'TD', 'TF', 'TG',
58: 'TH', 'TJ', 'TK', 'TM', 'TN', 'TO', 'TP', 'TR', 'TT', 'TV', 'TW',
59: 'TZ', 'UA', 'UG', 'UM', 'US', 'UY', 'UZ', 'VA', 'VC', 'VE', 'VG',
60: 'VI', 'VN', 'VU', 'WF', 'WS', 'YE', 'YT', 'YU', 'ZA', 'ZM', 'ZR',
61: 'ZW', 'A1', 'A2', 'O1'
62: );
63:
64: 65: 66: 67: 68:
69: protected $_datafile;
70:
71: 72: 73: 74: 75:
76: protected $_fh;
77:
78: 79: 80: 81: 82:
83: public function __construct($datafile)
84: {
85: $this->_datafile = $datafile;
86: }
87:
88: 89: 90: 91: 92:
93: protected function _open()
94: {
95:
96: if (!empty($this->_fh)) {
97: return true;
98: }
99:
100:
101: if (empty($this->_datafile)) {
102: return false;
103: }
104:
105: $this->_fh = fopen($this->_datafile, 'rb');
106: if (!$this->_fh) {
107: return false;
108: }
109:
110: $filepos = ftell($this->_fh);
111: fseek($this->_fh, -3, SEEK_END);
112: for ($i = 0; $i < self::STRUCTURE_INFO_MAX_SIZE; ++$i) {
113: $delim = fread($this->_fh, 3);
114: if ($delim == (chr(255) . chr(255) . chr(255))) {
115: break;
116: } else {
117: fseek($this->_fh, -4, SEEK_CUR);
118: }
119: }
120: fseek($this->_fh, $filepos, SEEK_SET);
121:
122: return true;
123: }
124:
125: 126: 127: 128: 129: 130: 131: 132:
133: public function getCountryInfo($name)
134: {
135: if (Horde_Util::extensionExists('geoip')) {
136: $id = @geoip_country_code_by_name($name);
137: $cname = @geoip_country_name_by_name($name);
138: return (!empty($id) && !empty($cname)) ?
139: array('code' => Horde_String::lower($id), 'name' => $cname):
140: false;
141: }
142:
143: $id = $this->countryIdByName($name);
144: if (!empty($id)) {
145: $code = $this->_countryCodes[$id];
146: return array(
147: 'code' => Horde_String::lower($code),
148: 'name' => $this->_getName($code)
149: );
150: }
151:
152: return false;
153: }
154:
155: 156: 157: 158: 159: 160: 161:
162: public function countryIdByName($name)
163: {
164: if (!$this->_open()) {
165: return false;
166: }
167:
168: $addr = gethostbyname($name);
169: if (!$addr || ($addr == $name)) {
170: return false;
171: }
172:
173: return $this->countryIdByAddr($addr);
174: }
175:
176: 177: 178: 179: 180: 181: 182:
183: public function countryCodeByName($name)
184: {
185: if ($this->_open()) {
186: $country_id = $this->countryIdByName($name);
187: if ($country_id !== false) {
188: return $this->_countryCodes[$country_id];
189: }
190: }
191:
192: return false;
193: }
194:
195: 196: 197: 198: 199: 200: 201:
202: public function countryNameByName($name)
203: {
204: if ($this->_open()) {
205: $country_id = $this->countryCodeByName($name);
206: if ($country_id !== false) {
207: return $this->_getName($country_id);
208: }
209: }
210:
211: return false;
212: }
213:
214: 215: 216: 217: 218: 219: 220:
221: public function countryIdByAddr($addr)
222: {
223: if (!$this->_open()) {
224: return false;
225: }
226:
227: $ipnum = ip2long($addr);
228: $country = $this->_seekCountry($ipnum);
229:
230: return ($country === false)
231: ? ''
232: : ($this->_seekCountry($ipnum) - self::GEOIP_COUNTRY_BEGIN);
233: }
234:
235: 236: 237: 238: 239: 240: 241:
242: public function countryCodeByAddr($addr)
243: {
244: if ($this->_open()) {
245: $country_id = $this->countryIdByAddr($addr);
246: if ($country_id !== false) {
247: return $this->_countryCodes[$country_id];
248: }
249: }
250:
251: return false;
252: }
253:
254: 255: 256: 257: 258: 259: 260:
261: public function countryNameByAddr($addr)
262: {
263: if ($this->_open()) {
264: $country_id = $this->countryCodeByAddr($addr);
265: if ($country_id !== false) {
266: return $this->_getName($country_id);
267: }
268: }
269:
270: return false;
271: }
272:
273: 274: 275: 276: 277: 278: 279:
280: protected function _seekCountry($ipnum)
281: {
282: $offset = 0;
283:
284: for ($depth = 31; $depth >= 0; --$depth) {
285: if (fseek($this->_fh, 2 * self::STANDARD_RECORD_LENGTH * $offset, SEEK_SET) != 0) {
286: return false;
287: }
288: $buf = fread($this->_fh, 2 * self::STANDARD_RECORD_LENGTH);
289: $x = array(0, 0);
290:
291: for ($i = 0; $i < 2; ++$i) {
292: for ($j = 0; $j < self::STANDARD_RECORD_LENGTH; ++$j) {
293: $x[$i] += ord($buf[self::STANDARD_RECORD_LENGTH * $i + $j]) << ($j * 8);
294: }
295: }
296: if ($ipnum & (1 << $depth)) {
297: if ($x[1] >= self::GEOIP_COUNTRY_BEGIN) {
298: return $x[1];
299: }
300: $offset = $x[1];
301: } else {
302: if ($x[0] >= self::GEOIP_COUNTRY_BEGIN) {
303: return $x[0];
304: }
305: $offset = $x[0];
306: }
307: }
308:
309: return false;
310: }
311:
312: 313: 314: 315: 316: 317: 318:
319: protected function _getName($code)
320: {
321: $code = Horde_String::upper($code);
322:
323: $geoip_codes = array(
324: 'AP' => Horde_Nls_Translation::t("Asia/Pacific Region"),
325: 'EU' => Horde_Nls_Translation::t("Europe"),
326: 'A1' => Horde_Nls_Translation::t("Anonymous Proxy"),
327: 'A2' => Horde_Nls_Translation::t("Satellite Provider"),
328: 'O1' => Horde_Nls_Translation::t("Other")
329: );
330:
331: return isset($geoip_codes[$code])
332: ? $geoip_codes[$code]
333: : strval(Horde_Nls::getCountryISO($code));
334: }
335:
336: }
337: