OSDN Git Service

Updating README.md
[rebornos/cnchi-gnome-osdn.git] / Cnchi / geoip.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 #  geoip.py
5 #
6 # Copyright © 2013-2019 RebornOS
7 #
8 # This file is part of Cnchi.
9 #
10 # Cnchi is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 3 of the License, or
13 # (at your option) any later version.
14 #
15 # Cnchi is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 # GNU General Public License for more details.
19 #
20 # The following additional terms are in effect as per Section 7 of the license:
21 #
22 # The preservation of all legal notices and author attributions in
23 # the material or in the Appropriate Legal Notices displayed
24 # by works containing it is required.
25 #
26 # You should have received a copy of the GNU General Public License
27 # along with Cnchi; If not, see <http://www.gnu.org/licenses/>.
28
29 """ GeoIP Location module
30     Needs python-geoip2 python-maxminddb geoip2-database """
31
32 import json
33 import logging
34 import os
35 import time
36 import requests
37
38 import maxminddb
39 import geoip2.database
40 import misc.extra as misc
41
42 class GeoIP():
43     """ Store GeoIP information """
44
45     REPO_CITY_DATABASE = '/usr/share/GeoIP/GeoLite2-City.mmdb'
46     LOCAL_CITY_DATABASE = '/usr/share/cnchi/data/GeoLite2-City.mmdb'
47
48     SERVERS = ["ipapi.com", "ipgeolocation.io"]
49
50     def __init__(self):
51         self.record = None
52         self._maybe_wait_for_network()
53         self._load_data_and_ip()
54
55     @staticmethod
56     def _maybe_wait_for_network():
57         # Wait until there is an Internet connection available
58         if not misc.has_connection():
59             logging.warning(
60                 "Can't get network status. Cnchi will try again in a moment")
61             while not misc.has_connection():
62                 time.sleep(4)  # Wait 4 seconds and try again
63
64         logging.debug("A working network connection has been detected.")
65
66     def _load_data_and_ip(self):
67         """ Gets public IP and loads GeoIP2 database """
68         db_path = GeoIP.REPO_CITY_DATABASE
69         if not os.path.exists(db_path):
70             db_path = GeoIP.LOCAL_CITY_DATABASE
71
72         if os.path.exists(db_path):
73             myip = self._get_external_ip()
74             logging.debug("Your external IP address is: %s", myip)
75             if myip:
76                 self._load_database(db_path, myip)
77                 if self.record:
78                     logging.debug("GeoIP database loaded (%s)", db_path)
79             else:
80                 logging.error("Cannot get your external IP address!")
81         else:
82             logging.error("Cannot find Cities GeoIP database")
83
84
85     @staticmethod
86     def _get_external_ip():
87         """ Get external IP """
88         for srv in GeoIP.SERVERS:
89             srv = "http://" + srv[::-1]
90             try:
91                 json_text = requests.get(srv).text
92                 if not "503 Over Quota" in json_text:
93                     data = json.loads(json_text)
94                     return data['ip']
95             except (requests.ConnectionError, json.decoder.JSONDecodeError) as err:
96                 logging.warning(
97                     "Error getting external IP from %s: %s", srv, err)
98         return None
99
100     def _load_database(self, db_path, myip):
101         """ Loads cities database """
102         try:
103             reader = geoip2.database.Reader(db_path)
104             self.record = reader.city(myip)
105         except maxminddb.errors.InvalidDatabaseError as err:
106             logging.error(err)
107
108     def get_city(self):
109         """ Returns city information
110             'city': {'geoname_id', 'names'} """
111         if self.record:
112             return self.record.city
113         return None
114
115     def get_country(self):
116         """ Returns country information
117             'country': {'geoname_id', 'is_in_european_union', 'iso_code', 'names'} """
118         if self.record:
119             return self.record.country
120         return None
121
122     def get_continent(self):
123         """ Returns continent information
124             'continent': {'code', 'geoname_id', 'names'} """
125         if self.record:
126             return self.record.continent
127         return None
128
129     def get_location(self):
130         """ Returns location information
131             'location': {'accuracy_radius', 'latitude', 'longitude', 'time_zone'} """
132         if self.record:
133             return self.record.location
134         return None
135
136
137
138 def test_module():
139     """ Test module """
140     geo = GeoIP()
141     print("City:", geo.get_city())
142     print("Country:", geo.get_country())
143     print("Continent:", geo.get_continent())
144     print("Location:", geo.get_location())
145
146
147 if __name__ == "__main__":
148     test_module()