# -*- Mode: Python; tab-width: 4 -*-
#
#    Author: Sam Rushing <rushing@nightmare.com>
#    Copyright 1996-2000 by Sam Rushing
#                         All Rights Reserved.
#

# support for 'basic' authenticaion.

from __future__ import print_function

import base64
import hashlib
import re
import string
import time

import counter

import default_handler

get_header = default_handler.get_header

import http_server

import producers

# This is a 'handler' that wraps an authorization method
# around access to the resources normally served up by
# another handler.

# does anyone support digest authentication? (rfc2069)


class auth_handler:
    def __init__(self, dict, handler, realm="default"):
        self.authorizer = dictionary_authorizer(dict)
        self.handler = handler
        self.realm = realm
        self.pass_count = counter.counter()
        self.fail_count = counter.counter()

    def match(self, request):
        # by default, use the given handler's matcher
        return self.handler.match(request)

    def handle_request(self, request):
        # authorize a request before handling it...
        scheme = get_header(AUTHORIZATION, request.header)

        if scheme:
            scheme = string.lower(scheme)
            if scheme == "basic":
                cookie = AUTHORIZATION.group(2)
                try:
                    decoded = base64.decodestring(cookie)
                except:
                    print("malformed authorization info <%s>" % cookie)
                    request.error(400)
                    return
                auth_info = string.split(decoded, ":")
                if self.authorizer.authorize(auth_info):
                    self.pass_count.increment()
                    request.auth_info = auth_info
                    self.handler.handle_request(request)
                else:
                    self.handle_unauthorized(request)
            # elif scheme == 'digest':
            #    print('digest: ',AUTHORIZATION.group(2))
            else:
                print("unknown/unsupported auth method: %s" % scheme)
                self.handle_unauthorized()
        else:
            # list both?  prefer one or the other?
            # you could also use a 'nonce' here. [see below]
            # auth = 'Basic realm="%s" Digest realm="%s"' %
            #    (self.realm, self.realm)
            # nonce = self.make_nonce (request)
            # auth = 'Digest realm="%s" nonce="%s"' % (self.realm, nonce)
            # request['WWW-Authenticate'] = auth
            # print('sending header: %s' % request['WWW-Authenticate'])
            self.handle_unauthorized(request)

    def handle_unauthorized(self, request):
        # We are now going to receive data that we want to ignore.
        # to ignore the file data we're not interested in.
        self.fail_count.increment()
        request.channel.set_terminator(None)
        request["Connection"] = "close"
        request["WWW-Authenticate"] = 'Basic realm="%s"' % self.realm
        request.error(401)

    def make_nonce(self, request):
        "A digest-authentication <nonce>, constructed as suggested in RFC 2069"
        ip = request.channel.server.ip
        now = str(long(time.time()))[:-1]
        private_key = str(id(self))
        nonce = string.join([ip, now, private_key], ":")
        return self.apply_hash(nonce)

    def apply_hash(self, s):
        "Apply MD5 to a string <s>, then wrap it in base64 encoding."
        m = hashlib.new()
        m.update(s)
        d = m.digest()
        # base64.encodestring tacks on an extra linefeed.
        return base64.encodestring(d)[:-1]

    def status(self):
        # Thanks to mwm@contessa.phone.net (Mike Meyer)
        r = [
            producers.simple_producer(
                "<li>Authorization Extension : "
                "<b>Unauthorized requests:</b> %s<ul>" % self.fail_count
            )
        ]
        if hasattr(self.handler, "status"):
            r.append(self.handler.status())
        r.append(producers.simple_producer("</ul>"))
        return producers.composite_producer(http_server.fifo(r))


class dictionary_authorizer:
    def __init__(self, dict):
        self.dict = dict

    def authorize(self, auth_info):
        [username, password] = auth_info
        if (username in self.dict) and (self.dict[username] == password):
            return 1
        else:
            return 0


AUTHORIZATION = re.compile(
    #               scheme  challenge
    "Authorization: ([^ ]+) (.*)",
    re.IGNORECASE,
)
