"""Main entry point for Railroad requests.

Entry points defined in this module:

authenhandler:
  Intercepts the authentication phase and is used to setup the Python side
  of the request handling. IOW does the initialization.

"""

# Copyright (c) 2004 Infrae. All rights reserved.
# See also LICENSE.txt

from mod_python import apache

import urllib
import md5
from datetime import datetime


import rr_config

import rr_util

###

def _make_id ( request ):
    t = datetime.now().isoformat()
##     rid = md5.new(t)
##     rid.update(request.uri)
##     rid.update(request.method)
##     rid.update(request.interpreter)
    rid = t + request.uri + request.method + request.interpreter
    return hex(hash(rid))
#

def cleanup ( request ):
    rr_util.ap_debug(request, 'CLEANUP (%s) %s' % (request.method, request.uri))
    rr_util.call_hooks('request-cleanup', request)
    return

def inithandler ( request ):
    """Prepare request for handling.

    Initialize the rr system if not already initialized.

    Register the cleanup function for this request.

    Assign a request id to the request so modules can track
    requests they're interested in.

    Call the 'request-start' hooks.

    Eventually fake the auth headers in case of GET request.
    """
    # prepare the request
    rid = _make_id(request)
    request.add_common_vars()
    request.request_id = rid
    rr_util.ap_debug(request, 'START (%s) %s' % (request.method, request.uri))
    request.register_cleanup(cleanup, request)
    if not rr_config.initialized:
        # first request, do initialization
        rr_util.init_railroad(request)
    rr_util.call_hooks('request-start', request)
    # Introduce fake Authorization header in case none is present.
    # This is used to circumvent the appearance of a login box even
    # when no password is required for a resource.
    # Applies only to GET requests!
    if request.method  != 'GET':
        return apache.OK
    headers = request.headers_in
    pw = request.get_basic_auth_pw()
    user = request.user
    if user or pw:
        return apache.OK
    # fake auth headers
    raw = ":".encode('base64')
    auth = 'Basic %s' % raw
    headers['Authorization'] = auth
    return apache.OK
#

headerparserhandler = inithandler
#

def authenhandler ( request ):
    """Do the authentication for this request.

    Assumes the inithandler function has setup everything,
    but checks to be sure. :)
    """
    # check if request set up properly
    if not hasattr(request, 'request_id'):
        # not setup, do so
        inithandler(request)
    if not (rr_config.do_auth or rr_config.do_search):
        # neither authentication nor search enabled
        # simply allow access
        return apache.OK
    # determine auth function for method
    method = request.method
    if method == 'POST':
        auth_fnc = auth_post
    else:
        auth_fnc = auth_normal
    ret = auth_fnc(request)
    return ret
#

def auth_normal ( request ):
    """Check if user credentials are ok for this request.
    """
    if not rr_config.do_auth:
        return apache.OK
    r_pw = request.get_basic_auth_pw()
    r_user = request.user
    repo_name = rr_config.repo_name
    try:
        repo_id, root_user, root_pw = rr_config.repo_master[repo_name]
    except KeyError:
        repo_id, root_user, root_pw = (None, None, None)
        pass
    if (r_user, r_pw) == (root_user, root_pw):
        # master access!
        rr_util.ap_notice(request, 'master access granted for %s' % request.uri)
        return apache.OK
    # get resource info from db
    path = request.uri
    resid, url, oid = rr_config.DB.get_resource_info(request, path, repo_name)
    # if we don't need authorization for this request
    # just allow access
    if resid and not url:
        # unowned resource, grant access
        return apache.OK
    elif not resid:
        # in case of errors or unknown resource deny access
        return apache.HTTP_UNAUTHORIZED
    url = url + '?' + urllib.urlencode([('id', oid), ('method', request.method)])
    ret = rr_util.call_cms(url, r_user, r_pw)
    if ret:
        return apache.OK
    return apache.HTTP_UNAUTHORIZED
#

def auth_post ( request ):
    """Authorization for post requests.

    This is a dummy function in this stage of the request.
    The real authorization is done during the request handling!
    """
    return apache.OK
#

def outputfilter ( filter ):
    """Intercept output of request and call 'outputfilter' hooks.

    The hook callables receive three parameters:
     1. the request object
     2. the path of the request
     3. the return body as COPY!


    The hook is not called for GET request,
    because these may contain *large* response
    bodies!
    """
    request = filter.req
    method = request.method
    if method == 'GET' and not request.header_only:
        # GET request
        # don't touch
        filter.pass_on()
        filter.flush()
        filter.close()
        return
    uri = request.uri
    data = []
    while True:
        s = filter.read(16384)
        if not s:
            break
        data.append(s)
    body = ''.join(data)
    del data
    # write unmodified data
    filter.write(body)
    filter.flush()
    filter.close()
    # call the hook
    rr_util.call_hooks('outputfilter', request, uri, body)
    return
#
###
