from trac.core import *
from trac.config import ListOption
from trac.web.api import IRequestFilter, RequestDone, IAuthenticator
from trac.web.chrome import INavigationContributor

try:  
    from base64 import b64decode  
except ImportError:  
    from base64 import decodestring as b64decode

from acct_mgr.api import AccountManager

__all__ = ['HTTPAuthFilter']

class HTTPAuthFilter(Component):
    """Request filter and handler to provide HTTP authentication."""
    
    paths = ListOption('httpauth', 'paths', default='/blog', doc='Paths to force HTTP authentication on.')
    
    formats = ListOption('httpauth', 'formats', default='rss', doc='Request formats to force HTTP authentication on')
    
    noredirectpaths = ListOption('httpauth', 'noredirectpaths', default='/login, /reset_password, /register, /chrome', doc='Paths excluded from redirection to login')
    
    implements(IRequestFilter, IAuthenticator)

    # IRequestFilter methods
    def pre_process_request(self, req, handler):
        # proceed if user is already authenticated
        if (req.authname and req.authname != 'anonymous'):
            return handler
        # test if HTTP Authentication is applicable 
        check = False
        for path in self.paths:
            if req.path_info.startswith(path):
                check = True
                break
        if req.args.get('format') in self.formats:
            check = True
        if check:
            if not self._check_password(req):
                self.log.info('HTTPAuthFilter: No/bad authentication data given, returing 403')
                return self
            else:
                return handle
        else:
            # check if paths are excluded from login, or redirect to login page
            for path in self.noredirectpaths:
                if req.path_info.startswith(path):
                    check = True
                    break
            if check:
                return handler
            else:
                self.log.debug('Redirecting anonymous request to /login')
                req.redirect(req.href.login() + '?referer=' + req.abs_href(req.path_info))
                return []
        
    def post_process_request(self, req, template, content_type):
        return template, content_type

    # IRequestHandler methods (sort of)       
    def process_request(self, req):
        if req.session:
            req.session.save() # Just in case
        
        req.send_response(401)
        req.send_header('WWW-Authenticate', 'Basic realm="Control Panel"')
        req.send_header('Content-Type', 'text/plain')
        req.send_header('Pragma', 'no-cache')
        req.send_header('Cache-control', 'no-cache')
        req.send_header('Expires', 'Fri, 01 Jan 1999 00:00:00 GMT')
        req.end_headers()
        
        if req.method != 'HEAD':
            req.write('Authentication required')
        raise RequestDone

    # IAuthenticator methods
    def authenticate(self, req):
        user = self._check_password(req)
        if user:
            self.log.debug('HTTPAuthFilter: Authentication okay for %s', user)
            return user
            
    # Internal methods
    def _check_password(self, req):
        header = req.get_header('Authorization')
        if header:
            token = header.split()[1]
            user, passwd = b64decode(token).split(':', 1)
            if AccountManager(self.env).check_password(user, passwd):
                return user

