#!/usr/bin/env python

# 
# A Name.com API molester
# 

# For managing DNS records hosted with name.com
# 

from sys import argv,exit
from getopt import getopt,GetoptError

import os
import re
import json
import string
import requests
import prettytable
import ConfigParser

def usage():
    print("""

        \033[33mName.com DNS Manager\033[0m\n

    Usage: ./namedotcom <option>
    <option> - can be any one of the following:
        --list-domains                              - list all of your domains
        --list-records <domain>                     - list all records in domain
        --create-record <name>,<domain>,<ip>,<type> - create subdomain for domain
        --delete-record <domain>,<record id>        - Delete a record

    """)

    exit(1)


# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
class Namedotcom():

    def __init__(self):
        """ Thanks for reading """
    
        self.token = ""
        self.base = "https://api.name.com"
        self.conffile = os.environ['HOME'] + "/.namedotcom.conf"

        self.allowed_chars = set(   string.ascii_letters + 
                                    string.digits        + 
                                    '-' + '.' + ':')

        self.config = ConfigParser.RawConfigParser()

        try:
            self.config.readfp( open(self.conffile,'rb') )
        except IOError as e:
            print("fatal: no configuration file found!\n({})".format(e))
            exit(1)
        
        self.api_username = self._getconf("username")
        self.api_token = self._getconf("api_token")
        
        self.headers = {}
        self.headers['Api-Username'] = self.api_username
        self.headers['Api-Token']    = self.api_token

# ----------------------------------------------------------------------------

    def _getconf(self, variable):
        try:
            return self.config.get("namedotcomconfig",variable)
        except ConfigParser.NoOptionError as e:
            print("Error parsing config file: {}".format(e))
            exit(1)

# ----------------------------------------------------------------------------

    def _validate_input(self, str_to_validate):
        """ Validates the user input, so we don't send the api nonsense """
        if set(str(str_to_validate)) - self.allowed_chars: 
            return False
        else:
            return str_to_validate

# ----------------------------------------------------------------------------

    def _list_domains(self):
        """ Returns a dict of domains """

        api_url = self.base + "/api/domain/list"
        try:
            result = requests.get(api_url, headers=self.headers)
        except Exception as e:
            print("fatal: API call failed: {}".format(e))
            exit(1)

        if result.status_code == 200:
            return result.json()
        else:
            print("fatal: the name.com api returned: {}".format(result))
            exit(1)

# ----------------------------------------------------------------------------

    def _list_records(self, domain):
        """ returns a dict of records """

        assert self._validate_input(domain), \
            "\n\n\nfatal: invalid domain name input '{}'\n\n".format(domain)
        domain_name = domain

        api_url = self.base + "/api/dns/list/" + domain_name
        try:
            result = requests.get(api_url, headers=self.headers)
        except Exception as e:
            print("fatal: API call failed: {}".format(e))
            exit(1)

        if result.status_code == 200:
            return result.json()
        else:
            print("fatal: the name.com api returned: {}".format(result))
            exit(1)

# ----------------------------------------------------------------------------

    def print_records(self, domain):
        records_json = self._list_records(domain)
        table = prettytable.PrettyTable([
                "\033[33mCreated\033[0m",
                "\033[33mName\033[0m",
                "\033[33mIP\033[0m",
                "\033[33mTTL\033[0m",
                "\033[33mRecord ID\033[0m",
                "\033[33mType\033[0m"
        ])
        for row in records_json["records"]:
            table.add_row([
            row["create_date"],
            row["name"],
            row["content"],
            row["ttl"],
            row["record_id"],
            row["type"]
            ])
        print(table)

# ----------------------------------------------------------------------------

    def print_domains(self):
        """ prettyprints the domain list """
    
        domain_json = self._list_domains() 
        table = prettytable.PrettyTable([
            "\033[33mDomain\033[0m", 
            "\033[33mCreated\033[0m", 
            "\033[33mExpire Date (YYYY/MM/DD)\033[0m"
        ])
        for domain_name, domain_info in domain_json["domains"].iteritems():
            table.add_row([
                    domain_name, 
                    domain_info["create_date"], 
                    domain_info["expire_date"]
            ])
        print(table)

# ----------------------------------------------------------------------------

    def create_record(  self, hostname_input, domain_input, ip_input, 
                        rec_type_input):

        domain_name = self._validate_input(domain_input)
        hostname = self._validate_input(hostname_input)
        domain   = self._validate_input(domain_input)
        ip       = self._validate_input(ip_input)
        rec_type = self._validate_input(rec_type_input)

        if False in ( domain_name, hostname, domain, ip, rec_type ):
            usage()

        url = self.base + "/api/dns/create/" + domain_name

        new_record_params = {}
        new_record_params["hostname"] = hostname
        new_record_params["type"]     = rec_type
        new_record_params["content"]  = ip
        new_record_params["ttl"]      = 300
        new_record_params["priority"] = 10

        response = requests.post(
            url, data=json.dumps(new_record_params),
            headers=self.headers
        )

        if response.status_code == 200:
            data = response.json()
            table = prettytable.PrettyTable(["Result"])
            table.add_row([data["result"]["message"]])
            print(table)
        else:
            exit(1)

# ----------------------------------------------------------------------------
        
    def delete_record(self, domain_input, rec_id_input):
        domain = self._validate_input(domain_input)
        rec_id = self._validate_input(rec_id_input)
        
        url = self.base + "/api/dns/delete/" + domain
        del_record_params = {}
        del_record_params["record_id"] = rec_id
        response = requests.post(
            url, data=json.dumps(del_record_params),
            headers=self.headers
        )
        if response.status_code == 200:
            data = response.json()
            table = prettytable.PrettyTable(["Result"])
            table.add_row([data["result"]["message"]])
            print(table)
        else:
            exit(1)


def main():

    if len(argv) <= 1:
        usage()

    try:
        opts,args = getopt(
            argv[1:], "h",
            [
                "help",
                "list-domains", 
                "list-records=",
                "create-record=",
                "delete-record="
            ]
        )
    except GetoptError as e:
        print(e)
        usage()

    if not opts:
        usage()

    api_handler = Namedotcom()
    for opt, arg in opts:
        if opt in ("-h", "--help"):
            usage()
        elif opt in ("--list-domains"):
            api_handler.print_domains()
        elif opt in ("--list-records"):
            api_handler.print_records(arg)
        elif opt in ("--create-record"):
            hostname, domain, ip, rec_type = arg.split(',')
            api_handler.create_record(hostname, domain, ip, rec_type)
        elif opt in ("--delete-record"):
            domain,rec_id = arg.split(',')
            api_handler.delete_record(domain, rec_id)
        else:
            usage()

if __name__ == "__main__":
    main()
