#!/usr/bin/env python
#
# Copyright (c) 2015-2018 Marian Andre, <marian@andre.sk>
#
# Version: 1.0.0-beta.1

from werkzeug.wrappers import Request, Response
import re
import dns.update
import dns.query
import dns.tsigkeyring
import binascii
import importlib


class ImproperlyConfigured(Exception):
    pass


class Configuration(object):

    def __init__(self):
        self.settings = None

    def configured(self):
        return self.settings is not None

    def configure(self):
        try:
            mod = importlib.import_module('settings')
        except ImportError as e:
            raise ImportError("Could not load settings module: %s", e)

        for setting in dir(mod):
            if setting.isupper() and not setting.startswith('#'):
                setting_value = getattr(mod, setting)
                setattr(self, setting, setting_value)


class DDNS(object):

    def __init__(self, config):
        self.dns_ip = config.DNS
        self.domain = config.DOMAIN
        try:
            self.keyring = dns.tsigkeyring.from_text({'update-key.': config.TSIG_KEY})
        except binascii.Error as e:
            self.keyring = None
            raise ImproperlyConfigured("Incorrect TSIG_KEY: %s", e)

    def dns_update(self, ip, host):
        rc = None

        if self.keyring is not None:
            update = dns.update.Update(self.domain, keyring=self.keyring)
            update.replace(host, 300, 'A', ip)
            response = dns.query.tcp(update, self.dns_ip, timeout=10)
            rc = response.rcode()
    
        return rc
           

class DynamicDNS(object):

    def __init__(self, config):
        self.ddns = DDNS(config)

    def sanity_check(self, ip, host):
        ip_check = r'^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$'
        host_check = r'^[a-zA-Z0-9-.]*$'
        return re.match(ip_check, ip) and re.match(host_check, host)
        
    def wsgi_app(self, environ, start_response):
        ip = None
        host = None
        in_args = None
        
        request = Request(environ)
        response = Response(response='', content_type='text/plain')
        
        if request.method == 'GET':
            in_args = request.args
        elif request.method == 'POST':
            in_args = request.form
            
        if 'ip' in in_args:
            ip = in_args['ip'].encode('utf-8')
        if 'host' in in_args:
            host = in_args['host'].encode('utf-8')

        if ip is not None and host is not None:
            if self.sanity_check(ip, host):
                self.ddns.dns_update(ip, host)
                response.data = 'OK'
                response.status_code = 200
            else:
                response.data = 'Wrong arguments'
                response.status_code = 400
        else:
            response.data = 'Missing arguments'
            response.status_code = 400
            
        return response(environ, start_response)

    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)


def create_app():
    configuration = Configuration()
    configuration.configure()
    a = DynamicDNS(configuration)
    return a


if __name__ == '__main__':
    from werkzeug.serving import run_simple
    app = create_app()
    run_simple('localhost', 8080, app)
