Overview

Packages

  • Nls

Classes

  • Horde_Nls
  • Horde_Nls_Geoip
  • Horde_Nls_Translation
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Horde optimized interface to the MaxMind IP Address->Country listing.
  4:  *
  5:  * Based on PHP geoip.inc library by MaxMind LLC:
  6:  *   http://www.maxmind.com/download/geoip/api/php/
  7:  *
  8:  * Originally based on php version of the geoip library written in May
  9:  * 2002 by jim winstead <jimw@apache.org>
 10:  *
 11:  * Copyright 2003 MaxMind LLC
 12:  * Copyright 2003-2012 Horde LLC (http://www.horde.org/)
 13:  *
 14:  * This library is free software; you can redistribute it and/or
 15:  * modify it under the terms of the GNU Lesser General Public
 16:  * License as published by the Free Software Foundation; either
 17:  * version 2.1 of the License, or (at your option) any later version.
 18:  *
 19:  * See the enclosed file COPYING for license information (LGPL). If you
 20:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 21:  *
 22:  * @author   Michael Slusarz <slusarz@horde.org>
 23:  * @category Horde
 24:  * @package  Nls
 25:  */
 26: class Horde_Nls_Geoip
 27: {
 28:     /* TODO */
 29:     const GEOIP_COUNTRY_BEGIN = 16776960;
 30:     const STRUCTURE_INFO_MAX_SIZE = 20;
 31:     const STANDARD_RECORD_LENGTH = 3;
 32: 
 33:     /**
 34:      * Country list.
 35:      *
 36:      * @var array
 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:      * The location of the GeoIP database.
 66:      *
 67:      * @var string
 68:      */
 69:     protected $_datafile;
 70: 
 71:     /**
 72:      * The open filehandle to the GeoIP database.
 73:      *
 74:      * @var resource
 75:      */
 76:     protected $_fh;
 77: 
 78:     /**
 79:      * Constructor.
 80:      *
 81:      * @param string $datafile         The location of the GeoIP database.
 82:      */
 83:     public function __construct($datafile)
 84:     {
 85:         $this->_datafile = $datafile;
 86:     }
 87: 
 88:     /**
 89:      * Open the GeoIP database.
 90:      *
 91:      * @return boolean  False on error.
 92:      */
 93:     protected function _open()
 94:     {
 95:         /* Return if we already have an object. */
 96:         if (!empty($this->_fh)) {
 97:             return true;
 98:         }
 99: 
100:         /* Return if no datafile specified. */
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:      * Returns the country ID and Name for a given hostname.
127:      *
128:      * @param string $name  The hostname.
129:      *
130:      * @return mixed  An array with 'code' as the country code and 'name' as
131:      *                the country name, or false if not found.
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:      * Returns the country ID for a hostname.
157:      *
158:      * @param string $name  The hostname.
159:      *
160:      * @return integer  The GeoIP country ID.
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:      * Returns the country abbreviation (2-letter) for a hostname.
178:      *
179:      * @param string $name  The hostname.
180:      *
181:      * @return integer  The country abbreviation.
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:      * Returns the country name for a hostname.
197:      *
198:      * @param string $name  The hostname.
199:      *
200:      * @return integer  The country name.
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:      * Returns the country ID for an IP Address.
216:      *
217:      * @param string $addr  The IP Address.
218:      *
219:      * @return integer  The GeoIP country ID.
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:      * Returns the country abbreviation (2-letter) for an IP Address.
237:      *
238:      * @param string $addr  The IP Address.
239:      *
240:      * @return integer  The country abbreviation.
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:      * Returns the country name for an IP address.
256:      *
257:      * @param string $addr  The IP address.
258:      *
259:      * @return mixed  The country name.
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:      * Finds a country by IP Address in the GeoIP database.
275:      *
276:      * @param string $ipnum  The IP Address to search for.
277:      *
278:      * @return mixed  The country ID or false if not found.
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:      * Given a 2-letter country code, returns a country string.
314:      *
315:      * @param string $code  The country code.
316:      *
317:      * @return string  The country string.
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: 
API documentation generated by ApiGen