PageRenderTime 29ms CodeModel.GetById 7ms RepoModel.GetById 1ms app.codeStats 0ms

/python-packages/django/contrib/gis/geoip/base.py

https://gitlab.com/gregtyka/ka-lite
Python | 265 lines | 237 code | 6 blank | 22 comment | 4 complexity | 441771f5fcf1f7f820aa79d98ab8ad84 MD5 | raw file
  1. import os
  2. import re
  3. from ctypes import c_char_p
  4. from django.core.validators import ipv4_re
  5. from django.contrib.gis.geoip.libgeoip import GEOIP_SETTINGS
  6. from django.contrib.gis.geoip.prototypes import (
  7. GeoIPRecord, GeoIPTag, GeoIP_open, GeoIP_delete, GeoIP_database_info,
  8. GeoIP_lib_version, GeoIP_record_by_addr, GeoIP_record_by_name,
  9. GeoIP_country_code_by_addr, GeoIP_country_code_by_name,
  10. GeoIP_country_name_by_addr, GeoIP_country_name_by_name)
  11. from django.utils import six
  12. # Regular expressions for recognizing the GeoIP free database editions.
  13. free_regex = re.compile(r'^GEO-\d{3}FREE')
  14. lite_regex = re.compile(r'^GEO-\d{3}LITE')
  15. #### GeoIP classes ####
  16. class GeoIPException(Exception): pass
  17. class GeoIP(object):
  18. # The flags for GeoIP memory caching.
  19. # GEOIP_STANDARD - read database from filesystem, uses least memory.
  20. #
  21. # GEOIP_MEMORY_CACHE - load database into memory, faster performance
  22. # but uses more memory
  23. #
  24. # GEOIP_CHECK_CACHE - check for updated database. If database has been
  25. # updated, reload filehandle and/or memory cache. This option
  26. # is not thread safe.
  27. #
  28. # GEOIP_INDEX_CACHE - just cache the most frequently accessed index
  29. # portion of the database, resulting in faster lookups than
  30. # GEOIP_STANDARD, but less memory usage than GEOIP_MEMORY_CACHE -
  31. # useful for larger databases such as GeoIP Organization and
  32. # GeoIP City. Note, for GeoIP Country, Region and Netspeed
  33. # databases, GEOIP_INDEX_CACHE is equivalent to GEOIP_MEMORY_CACHE
  34. #
  35. # GEOIP_MMAP_CACHE - load database into mmap shared memory ( not available
  36. # on Windows).
  37. GEOIP_STANDARD = 0
  38. GEOIP_MEMORY_CACHE = 1
  39. GEOIP_CHECK_CACHE = 2
  40. GEOIP_INDEX_CACHE = 4
  41. GEOIP_MMAP_CACHE = 8
  42. cache_options = dict((opt, None) for opt in (0, 1, 2, 4, 8))
  43. # Paths to the city & country binary databases.
  44. _city_file = ''
  45. _country_file = ''
  46. # Initially, pointers to GeoIP file references are NULL.
  47. _city = None
  48. _country = None
  49. def __init__(self, path=None, cache=0, country=None, city=None):
  50. """
  51. Initializes the GeoIP object, no parameters are required to use default
  52. settings. Keyword arguments may be passed in to customize the locations
  53. of the GeoIP data sets.
  54. * path: Base directory to where GeoIP data is located or the full path
  55. to where the city or country data files (*.dat) are located.
  56. Assumes that both the city and country data sets are located in
  57. this directory; overrides the GEOIP_PATH settings attribute.
  58. * cache: The cache settings when opening up the GeoIP datasets,
  59. and may be an integer in (0, 1, 2, 4, 8) corresponding to
  60. the GEOIP_STANDARD, GEOIP_MEMORY_CACHE, GEOIP_CHECK_CACHE,
  61. GEOIP_INDEX_CACHE, and GEOIP_MMAP_CACHE, `GeoIPOptions` C API
  62. settings, respectively. Defaults to 0, meaning that the data is read
  63. from the disk.
  64. * country: The name of the GeoIP country data file. Defaults to
  65. 'GeoIP.dat'; overrides the GEOIP_COUNTRY settings attribute.
  66. * city: The name of the GeoIP city data file. Defaults to
  67. 'GeoLiteCity.dat'; overrides the GEOIP_CITY settings attribute.
  68. """
  69. # Checking the given cache option.
  70. if cache in self.cache_options:
  71. self._cache = cache
  72. else:
  73. raise GeoIPException('Invalid GeoIP caching option: %s' % cache)
  74. # Getting the GeoIP data path.
  75. if not path:
  76. path = GEOIP_SETTINGS.get('GEOIP_PATH', None)
  77. if not path: raise GeoIPException('GeoIP path must be provided via parameter or the GEOIP_PATH setting.')
  78. if not isinstance(path, six.string_types):
  79. raise TypeError('Invalid path type: %s' % type(path).__name__)
  80. if os.path.isdir(path):
  81. # Constructing the GeoIP database filenames using the settings
  82. # dictionary. If the database files for the GeoLite country
  83. # and/or city datasets exist, then try and open them.
  84. country_db = os.path.join(path, country or GEOIP_SETTINGS.get('GEOIP_COUNTRY', 'GeoIP.dat'))
  85. if os.path.isfile(country_db):
  86. self._country = GeoIP_open(country_db, cache)
  87. self._country_file = country_db
  88. city_db = os.path.join(path, city or GEOIP_SETTINGS.get('GEOIP_CITY', 'GeoLiteCity.dat'))
  89. if os.path.isfile(city_db):
  90. self._city = GeoIP_open(city_db, cache)
  91. self._city_file = city_db
  92. elif os.path.isfile(path):
  93. # Otherwise, some detective work will be needed to figure
  94. # out whether the given database path is for the GeoIP country
  95. # or city databases.
  96. ptr = GeoIP_open(path, cache)
  97. info = GeoIP_database_info(ptr)
  98. if lite_regex.match(info):
  99. # GeoLite City database detected.
  100. self._city = ptr
  101. self._city_file = path
  102. elif free_regex.match(info):
  103. # GeoIP Country database detected.
  104. self._country = ptr
  105. self._country_file = path
  106. else:
  107. raise GeoIPException('Unable to recognize database edition: %s' % info)
  108. else:
  109. raise GeoIPException('GeoIP path must be a valid file or directory.')
  110. def __del__(self):
  111. # Cleaning any GeoIP file handles lying around.
  112. if self._country: GeoIP_delete(self._country)
  113. if self._city: GeoIP_delete(self._city)
  114. def _check_query(self, query, country=False, city=False, city_or_country=False):
  115. "Helper routine for checking the query and database availability."
  116. # Making sure a string was passed in for the query.
  117. if not isinstance(query, six.string_types):
  118. raise TypeError('GeoIP query must be a string, not type %s' % type(query).__name__)
  119. # GeoIP only takes ASCII-encoded strings.
  120. query = query.encode('ascii')
  121. # Extra checks for the existence of country and city databases.
  122. if city_or_country and not (self._country or self._city):
  123. raise GeoIPException('Invalid GeoIP country and city data files.')
  124. elif country and not self._country:
  125. raise GeoIPException('Invalid GeoIP country data file: %s' % self._country_file)
  126. elif city and not self._city:
  127. raise GeoIPException('Invalid GeoIP city data file: %s' % self._city_file)
  128. # Return the query string back to the caller.
  129. return query
  130. def city(self, query):
  131. """
  132. Returns a dictionary of city information for the given IP address or
  133. Fully Qualified Domain Name (FQDN). Some information in the dictionary
  134. may be undefined (None).
  135. """
  136. query = self._check_query(query, city=True)
  137. if ipv4_re.match(query):
  138. # If an IP address was passed in
  139. return GeoIP_record_by_addr(self._city, c_char_p(query))
  140. else:
  141. # If a FQDN was passed in.
  142. return GeoIP_record_by_name(self._city, c_char_p(query))
  143. def country_code(self, query):
  144. "Returns the country code for the given IP Address or FQDN."
  145. query = self._check_query(query, city_or_country=True)
  146. if self._country:
  147. if ipv4_re.match(query):
  148. return GeoIP_country_code_by_addr(self._country, query)
  149. else:
  150. return GeoIP_country_code_by_name(self._country, query)
  151. else:
  152. return self.city(query)['country_code']
  153. def country_name(self, query):
  154. "Returns the country name for the given IP Address or FQDN."
  155. query = self._check_query(query, city_or_country=True)
  156. if self._country:
  157. if ipv4_re.match(query):
  158. return GeoIP_country_name_by_addr(self._country, query)
  159. else:
  160. return GeoIP_country_name_by_name(self._country, query)
  161. else:
  162. return self.city(query)['country_name']
  163. def country(self, query):
  164. """
  165. Returns a dictonary with with the country code and name when given an
  166. IP address or a Fully Qualified Domain Name (FQDN). For example, both
  167. '24.124.1.80' and 'djangoproject.com' are valid parameters.
  168. """
  169. # Returning the country code and name
  170. return {'country_code' : self.country_code(query),
  171. 'country_name' : self.country_name(query),
  172. }
  173. #### Coordinate retrieval routines ####
  174. def coords(self, query, ordering=('longitude', 'latitude')):
  175. cdict = self.city(query)
  176. if cdict is None: return None
  177. else: return tuple(cdict[o] for o in ordering)
  178. def lon_lat(self, query):
  179. "Returns a tuple of the (longitude, latitude) for the given query."
  180. return self.coords(query)
  181. def lat_lon(self, query):
  182. "Returns a tuple of the (latitude, longitude) for the given query."
  183. return self.coords(query, ('latitude', 'longitude'))
  184. def geos(self, query):
  185. "Returns a GEOS Point object for the given query."
  186. ll = self.lon_lat(query)
  187. if ll:
  188. from django.contrib.gis.geos import Point
  189. return Point(ll, srid=4326)
  190. else:
  191. return None
  192. #### GeoIP Database Information Routines ####
  193. @property
  194. def country_info(self):
  195. "Returns information about the GeoIP country database."
  196. if self._country is None:
  197. ci = 'No GeoIP Country data in "%s"' % self._country_file
  198. else:
  199. ci = GeoIP_database_info(self._country)
  200. return ci
  201. @property
  202. def city_info(self):
  203. "Retuns information about the GeoIP city database."
  204. if self._city is None:
  205. ci = 'No GeoIP City data in "%s"' % self._city_file
  206. else:
  207. ci = GeoIP_database_info(self._city)
  208. return ci
  209. @property
  210. def info(self):
  211. "Returns information about the GeoIP library and databases in use."
  212. info = ''
  213. if GeoIP_lib_version:
  214. info += 'GeoIP Library:\n\t%s\n' % GeoIP_lib_version()
  215. return info + 'Country:\n\t%s\nCity:\n\t%s' % (self.country_info, self.city_info)
  216. #### Methods for compatibility w/the GeoIP-Python API. ####
  217. @classmethod
  218. def open(cls, full_path, cache):
  219. return GeoIP(full_path, cache)
  220. def _rec_by_arg(self, arg):
  221. if self._city:
  222. return self.city(arg)
  223. else:
  224. return self.country(arg)
  225. region_by_addr = city
  226. region_by_name = city
  227. record_by_addr = _rec_by_arg
  228. record_by_name = _rec_by_arg
  229. country_code_by_addr = country_code
  230. country_code_by_name = country_code
  231. country_name_by_addr = country_name
  232. country_name_by_name = country_name