#!/usr/bin/env python3

"""A Python program to get the expiration date of a domain name,
through RDAP."""

import sys
import json
import datetime
import getopt
import re

# http://python-requests.org/ for easier HTTPS retrieval
import requests

# Local module
import ianardap

# Funny diversity in RDAP servers' output...
RFC3339 = "%Y-%m-%dT%H:%M:%SZ%z"
RFC3339TZ = "%Y-%m-%dT%H:%M:%S%z" # We cannot use it directly, Python uses HHMM as offset, not HH:MM as RFC 3339 does :-(
RFC3339LONG = "%Y-%m-%dT%H:%M:%S.%fZ%z" # Microseconds support (%f) does not seem documented

UTCINFO = datetime.timezone(offset=datetime.timedelta(seconds=0))

# Do not touch
# https://www.monitoring-plugins.org/doc/guidelines.html#AEN78
STATE_OK = 0
STATE_WARNING = 1
STATE_CRITICAL = 2
STATE_UNKNOWN = 3
STATE_DEPENDENT = 4

# Can be changed on the command-line
verbose = False
critical = datetime.timedelta(days=2)
warning = datetime.timedelta(days=7)

def usage(msg=None):
    print("Usage: %s domain-name" % sys.argv[0], end="")
    if msg is not None and msg != "":
        print(" (%s)" % msg)
    else:
        print("")

try:
    optlist, args = getopt.getopt (sys.argv[1:], "c:hvw:",
                                   ["help", "verbose", "critical=", "warning="])
    for option, value in optlist:
        if option == "--help" or option == "-h":
            usage()
            sys.exit(STATE_OK)
        elif option == "--verbose" or option == "-v":
            verbose = True
except getopt.error as reason:
    usage(reason)
    sys.exit(STATE_UNKNOWN)
if len(args) != 1:
    usage()
    sys.exit(STATE_UNKNOWN)
domain = args[0]

database = ianardap.IanaRDAPDatabase()
if verbose:
    print("Using database \"%s\", version %s published on %s" % \
          (database.description, database.version, database.publication))
server = database.find(domain)
if server is None:
    raise Exception("No RDAP server found for %s" % domain)
if server.endswith("/"):
    server = server[:-1] # Donuts RDAP server balks when there are two slashes and reply 404
if verbose:
    print("RDAP server is %s" % server)
response = requests.get("%s/domain/%s" % (server, domain))
if response.status_code != 200:
    raise Exception("Invalid RDAP return code: %s" % response.status_code)
rdap = json.loads(response.content)
for event in rdap["events"]:
    if event["eventAction"] == "expiration":
        try:
            expiration = datetime.datetime.strptime(event["eventDate"] + "+0000", RFC3339LONG)
        except ValueError:
            try:
                expiration = datetime.datetime.strptime(event["eventDate"] + "+0000", RFC3339)
            except ValueError:
                # Python standard library offers no way to parse all RFC3339-compliant datetimes, so we have to hack.
                if re.search(r"\+[0-9]{2}:[0-9]{2}$", event["eventDate"]):
                    dt = re.sub(r"\+([0-9]{2}):([0-9]{2})$", r"+\1\2",  event["eventDate"])
                    expiration = datetime.datetime.strptime(dt, RFC3339TZ)
                else:
                    print("No recognized format for datetime \"%s\"" % event["eventDate"])
                    sys.exit(STATE_UNKNOWN)
        now = datetime.datetime.now(tz=UTCINFO)
        if expiration < now:
            print("CRITICAL: domain %s is already expired (%s ago)" % (domain, (now-expiration)))
            sys.exit(STATE_CRITICAL)            
        rest = expiration-now
        if rest < critical:
            print("CRITICAL: domain %s expires in %s, HURRY UP!!!" % (domain, rest))
            sys.exit(STATE_CRITICAL)
        elif rest < warning:
            print("WARNING: domain %s expires in %s, renew now" % (domain, rest))
            sys.exit(STATE_WARNING)
        else:
            print("OK: domain %s expires in %s" % (domain, rest))
            sys.exit(STATE_OK)
raise Exception("No expiration found")

    
