tools.py 4.06 KB
#!/usr/bin/python3
"""
module containing all the little sub routines
"""


from collections import OrderedDict
from datetime import datetime
import json
import socket


import geoip2.database
import geoip2.errors


from ipinfo import settings


# init geoip2 database reader
geo_reader = geoip2.database.Reader(settings.GEOLITE_DB)


def get_ip(request):
    """
    gets an ip address, based on a request.
    Filters for reverse proxies
    """

    # taken from http://stackoverflow.com/a/22936947
    route = request.access_route + [request.remote_addr]
    return next((addr for addr in reversed(route)
                 if addr not in settings.TRUSTED_PROXIES), request.remote_addr)


def reverse_ip(ip):
    try:
        name, alias, addresslist = socket.gethostbyaddr(ip)
        return name
    except socket.herror:
        # could not reverse-resolve this ip
        return None


def query_data(request, query):
    """
    creates the data dict, based on the query
    """
    # populate data
    data = OrderedDict()

    if not query or 'query' in query:
        data['query'] = query

    if not query or 'ip' in query:
        data['ip'] = get_ip(request)
    if not query or 'dns' in query:
        ip = data['ip'] if 'ip' in data else get_ip(request)
        data['dns'] = reverse_ip(ip)
    if not query or 'reqmethod' in query:
        data['request-method'] = request.method
    if not query or 'useragent' in query:
        data['user-agent'] = request.headers.get('User-Agent', None)
    if not query or 'time' in query:
        data['time'] = datetime.now().isoformat()
    if not query or any(k in query for k in ('geo', 'latitude', 'longitude', 'city', 'country', 'country-subdivision', 'continent', 'timezone')):
        # acquire geo data
        try:
            ip = data['ip'] if 'ip' in data else get_ip(request)
            # ip = '128.101.101.101'  # test ip
            location = geo_reader.city(ip)
        except geoip2.errors.AddressNotFoundError:
            # addr is not in the database
            location = None

        # check for universal query 'geo'
        if query and 'geo' in query:
            query.extend(['latitude', 'longitude', 'city', 'country', 'country-subdivision', 'continent', 'timezone'])

        if not query or 'latitude' in query:
            data['latitude'] = location.location.latitude if location else None
        if not query or 'longitude' in query:
            data['longitude'] = location.location.longitude if location else None
        if not query or 'city' in query:
            data['city'] = location.city.name if location and location.city else None
        if not query or 'country' in query:
            data['country'] = (location.country.name, location.country.iso_code) if location and location.country else None
        if not query or 'country-subdivion' in query:
            data['country-subdivision'] = (location.subdivisions.most_specific.name, location.subdivisions.most_specific.iso_code) if location and location.subdivisions else None
        if not query or 'continent' in query:
            data['continent'] = (location.continent.name, location.continent.code) if location and location.continent else None
        if not query or 'timezone' in query:
            data['timezone'] = location.location.time_zone if location else None

        return data


def guess_format(request):
    """
    guesses the format based on the Accept header
    """
    return 'txt'  # TODO


def format_text(data, justify=30):

    if isinstance(data, (dict, OrderedDict)):
        # data is map
        resp = []
        for key, value in data.items():
            value = format_text(value, justify=justify).split('\n')
            resp.append('\n'.join([(key.ljust(justify) + value[0])] + list(map(lambda v: ' '*justify+v, value[1:]))))

        return '\n'.join(resp)

    elif isinstance(data, (list, tuple)):
        # data is in list format
        return '\n'.join(data)

    else:
        # just some random data -> return it as string
        return str(data)


def format_json(data, justify=2):

    return json.dumps(data, sort_keys=False, indent=justify)