#!/usr/bin/python
import errno, httplib, logging, select, socket
from BaseHTTPServer import BaseHTTPRequestHandler
from SocketServer import ThreadingTCPServer
from urlparse import parse_qsl
from re6st import registry, utils

# To generate server ca and key with serial for 2001:db8:42::/48
#  openssl req -nodes -new -x509 -key ca.key -set_serial 0x120010db80042 -days 3650 -out ca.crt
# TODO: There is currently no mechanism so that client automatically get
#       a renewed CA certificate.

IPV6_V6ONLY = 26
SOL_IPV6 = 41


class RequestHandler(BaseHTTPRequestHandler):

    def address_string(self):
        # Workaround for http://bugs.python.org/issue6085
        return self.client_address[0]

    def do_GET(self):
        try:
            try:
                path, query = self.path.split('?', 1)
            except ValueError:
                path = self.path
                query = {}
            else:
                query = dict(parse_qsl(query, keep_blank_values=1,
                                              strict_parsing=1))
            _, path = path.split('/')
            if not _ and path[0] != '_':
                return self.server._handle_request(self, path, query)
        except Exception:
            logging.info(self.requestline, exc_info=1)
        self.send_error(httplib.BAD_REQUEST)

    def log_error(*args):
        pass


class HTTPServer4(ThreadingTCPServer):

    allow_reuse_address = True
    daemon_threads = True


class HTTPServer6(HTTPServer4):

    address_family = socket.AF_INET6

    def server_bind(self):
        self.socket.setsockopt(SOL_IPV6, IPV6_V6ONLY, 1)
        HTTPServer4.server_bind(self)


def main():
    parser = utils.ArgParser(fromfile_prefix_chars='@',
        description="re6stnet registry used to bootstrap nodes"
                    " and deliver certificates.")
    _ = parser.add_argument
    _('--port', type=int, default=80,
        help="Port on which the server will listen.")
    _('-4', dest='bind4', default='0.0.0.0',
        help="Bind server to this IPv4.")
    _('-6', dest='bind6', default='::',
        help="Bind server to this IPv6.")
    _('--db', default='/var/lib/re6stnet/registry.db',
        help="Path to SQLite database file. It is automatically initialized"
             " if the file does not exist.")
    _('--ca', required=True, help=parser._ca_help)
    _('--key', required=True,
            help="CA private key in .pem format.")
    _('--mailhost', required=True,
            help="SMTP host to send confirmation emails. For debugging"
                 " purpose, it can also be an absolute or existing path to"
                 " a mailbox file")
    _('--private',
            help="re6stnet IP of the node on which runs the registry."
                 " Required for normal operation.")
    _('--prefix-length', default=16, type=int,
            help="Default length of allocated prefixes.")
    _('--anonymous-prefix-length', type=int,
            help="Length of allocated anonymous prefixes."
                 " If 0 or unset, registration by email is required")
    _('-l', '--logfile', default='/var/log/re6stnet/registry.log',
            help="Path to logging file.")
    _('-v', '--verbose', default=1, type=int,
            help="Log level. 0 disables logging."
                 " Use SIGUSR1 to reopen log.")
    config = parser.parse_args()

    utils.setupLog(config.verbose, config.logfile)

    server = registry.RegistryServer(config)
    def requestHandler(request, client_address, _):
        RequestHandler(request, client_address, server)

    server_list = []
    if config.bind4:
        server_list.append(HTTPServer4((config.bind4, config.port),
                                       requestHandler))
    if config.bind6:
        server_list.append(HTTPServer6((config.bind6, config.port),
                                       requestHandler))
    if server_list:
        empty_list = []
        while True:
            try:
                r = select.select(server_list[:], empty_list, empty_list)[0]
            except select.error as e:
                if e.args[0] != errno.EINTR:
                    raise
            else:
                for r in r:
                    r._handle_request_noblock()


if __name__ == "__main__":
    main()