sopel.modules.ip._decompress()   A
last analyzed

Complexity

Conditions 4

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
dl 0
loc 10
rs 10
c 0
b 0
f 0
cc 4
nop 3
1
# coding=utf-8
2
"""
3
ip.py - Sopel GeoIP Lookup Module
4
Copyright 2011, Dimitri Molenaars, TyRope.nl,
5
Copyright © 2013, Elad Alfassa <[email protected]>
6
Licensed under the Eiffel Forum License 2.
7
8
https://sopel.chat
9
"""
10
11
from __future__ import unicode_literals, absolute_import, print_function, division
12
13
import logging
14
import os
15
import socket
16
import tarfile
17
18
import geoip2.database
19
20
from sopel.config.types import FilenameAttribute, StaticSection
21
from sopel.module import commands, example
22
23
urlretrieve = None
24
try:
25
    from urllib import urlretrieve
26
except ImportError:
27
    try:
28
        # urlretrieve has been put under urllib.request in Python 3.
29
        # It's also deprecated so this should probably be replaced with
30
        # urllib2.
31
        from urllib.request import urlretrieve
32
    except ImportError:
33
        pass
34
35
36
LOGGER = logging.getLogger(__name__)
37
38
39
class GeoipSection(StaticSection):
40
    GeoIP_db_path = FilenameAttribute('GeoIP_db_path', directory=True)
41
    """Path of the directory containing the GeoIP database files."""
42
43
44
def configure(config):
45
    """
46
    | name | example | purpose |
47
    | ---- | ------- | ------- |
48
    | GeoIP\\_db\\_path | /home/sopel/GeoIP/ | Path to the GeoIP database files |
49
    """
50
    config.define_section('ip', GeoipSection)
51
    config.ip.configure_setting('GeoIP_db_path',
52
                                'Path of the GeoIP db files')
53
54
55
def setup(bot):
56
    bot.config.define_section('ip', GeoipSection)
57
58
59
def _decompress(source, target, delete_after_decompression=True):
60
    """Decompress just the database from the archive"""
61
    # https://stackoverflow.com/a/16452962
62
    tar = tarfile.open(source)
63
    for member in tar.getmembers():
64
        if ".mmdb" in member.name:
65
            member.name = os.path.basename(member.name)
66
            tar.extract(member, target)
67
    if delete_after_decompression:
68
        os.remove(source)
69
70
71
def _find_geoip_db(bot):
72
    """Find the GeoIP database"""
73
    config = bot.config
74
    if config.ip.GeoIP_db_path:
75
        cities_db = os.path.join(config.ip.GeoIP_db_path, 'GeoLite2-City.mmdb')
76
        ipasnum_db = os.path.join(config.ip.GeoIP_db_path, 'GeoLite2-ASN.mmdb')
77
        if (os.path.isfile(cities_db) and os.path.isfile(ipasnum_db)):
78
            return config.ip.GeoIP_db_path
79
        else:
80
            LOGGER.warning(
81
                'GeoIP path configured but DB not found in configured path')
82
83
    if (os.path.isfile(os.path.join(config.core.homedir, 'GeoLite2-City.mmdb')) and
84
            os.path.isfile(os.path.join(config.core.homedir, 'GeoLite2-ASN.mmdb'))):
85
        return config.core.homedir
86
    elif (os.path.isfile(os.path.join('/usr/share/GeoIP', 'GeoLite2-City.mmdb')) and
87
            os.path.isfile(os.path.join('/usr/share/GeoIP', 'GeoLite2-ASN.mmdb'))):
88
        return '/usr/share/GeoIP'
89
    elif urlretrieve:
90
        LOGGER.info('Downloading GeoIP database')
91
        bot.say('Downloading GeoIP database, please wait...')
92
        geolite_urls = [
93
            'https://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz',
94
            'https://geolite.maxmind.com/download/geoip/database/GeoLite2-ASN.tar.gz'
95
        ]
96
        for url in geolite_urls:
97
            LOGGER.debug('GeoIP Source URL: %s', url)
98
            full_path = os.path.join(config.core.homedir, url.split("/")[-1])
99
            urlretrieve(url, full_path)
100
            _decompress(full_path, config.core.homedir)
101
        return bot.config.core.homedir
102
    else:
103
        return False
104
105
106
@commands('iplookup', 'ip')
107
@example('.ip 8.8.8.8',
108
         r'\[IP\/Host Lookup\] Hostname: \S*dns\S*\.google\S* \| Location: United States \| ISP: AS15169 Google LLC',
109
         re=True,
110
         ignore='Downloading GeoIP database, please wait...',
111
         online=True)
112
def ip(bot, trigger):
113
    """IP Lookup tool"""
114
    # Check if there is input at all
115
    if not trigger.group(2):
116
        return bot.reply("No search term.")
117
    # Check whether the input is an IP or hostmask or a nickname
118
    decide = ['.', ':']
119
    if any(x in trigger.group(2) for x in decide):
120
        # It's an IP/hostname!
121
        query = trigger.group(2).strip()
122
    else:
123
        # Need to get the host for the username
124
        username = trigger.group(2).strip()
125
        user_in_botdb = bot.users.get(username)
126
        if user_in_botdb is not None:
127
            query = user_in_botdb.host
128
129
            # Sanity check - sometimes user information isn't populated yet
130
            if query is None:
131
                return bot.say("I don't know that user's host.")
132
        else:
133
            return bot.say("I\'m not aware of this user.")
134
135
    db_path = _find_geoip_db(bot)
136
    if db_path is False:
137
        LOGGER.error('Can\'t find (or download) usable GeoIP database.')
138
        bot.say('Sorry, I don\'t have a GeoIP database to use for this lookup.')
139
        return False
140
141
    if ':' in query:
142
        try:
143
            socket.inet_pton(socket.AF_INET6, query)
144
        except (OSError, socket.error):  # Python 2/3 compatibility
145
            return bot.say("[IP/Host Lookup] Unable to resolve IP/Hostname")
146
    elif '.' in query:
147
        try:
148
            socket.inet_pton(socket.AF_INET, query)
149
        except (socket.error, socket.herror):
150
            try:
151
                query = socket.getaddrinfo(query, None)[0][4][0]
152
            except socket.gaierror:
153
                return bot.say("[IP/Host Lookup] Unable to resolve IP/Hostname")
154
    else:
155
        return bot.say("[IP/Host Lookup] Unable to resolve IP/Hostname")
156
157
    city = geoip2.database.Reader(os.path.join(db_path, 'GeoLite2-City.mmdb'))
158
    asn = geoip2.database.Reader(os.path.join(db_path, 'GeoLite2-ASN.mmdb'))
159
    host = socket.getfqdn(query)
160
    try:
161
        city_response = city.city(query)
162
        asn_response = asn.asn(query)
163
    except geoip2.errors.AddressNotFoundError:
164
        return bot.say("[IP/Host Lookup] The address is not in the database.")
165
166
    response = "[IP/Host Lookup] Hostname: %s" % host
167
    try:
168
        response += " | Location: %s" % city_response.country.name
169
    except AttributeError:
170
        response += ' | Location: Unknown'
171
172
    region = city_response.subdivisions.most_specific.name
173
    response += " | Region: %s" % region if region else ""
174
    city = city_response.city.name
175
    response += " | City: %s" % city if city else ""
176
    isp = "AS" + str(asn_response.autonomous_system_number) + \
177
          " " + asn_response.autonomous_system_organization
178
    response += " | ISP: %s" % isp if isp else ""
179
    bot.say(response)
180
181
182
if __name__ == "__main__":
183
    from sopel.test_tools import run_example_tests
184
    run_example_tests(__file__)
185