# -*- coding: iso-8859-1 -*-
"""
    MoinMoin - auth plugin using remote http(s) server with
    authentication.
    Based on a code found here:
    http://www.voidspace.org.uk/python/articles/authentication.shtml
    
    @copyright: 2009 Jakub Schmidtke <sjakub-at-gmail.com>
    @license: GNU GPL, see COPYING for details.
"""

import urllib2
import sys
import re
import base64
from urlparse import urlparse

from MoinMoin import config, user

from MoinMoin.auth import BaseAuth, CancelLogin, ContinueLogin

class RemoteHttpAuth(BaseAuth):
    """ 
        Auth plugin using remote http(s) authentication
    """
    name = 'remote_http'
    login_inputs = ['username', 'password']
    logout_possible = True
        
    def __init__(self,
        auth_url,                    # url used for authentication:
                                     # http(s)://server_with_authentication/some_page
        auth_hint = None,            # login hint        
        required_web_content = None, # Content required from URL if properly authenticated.
                                     # Content fetched from URL should be equal to this value\
                                     # (if specified, if not this test is not performed)
	auto_email_domain = None,    # if set, it will be used for registering automatic
	                             # email for new users, in form: username@auto_email_domain
	autocreate = False,          # should new users be created?
        ):
        BaseAuth.__init__(self)
        self.auth_url = auth_url
        self.auth_hint = auth_hint
        self.required_web_content = required_web_content
	self.auto_email_domain = auto_email_domain
        self.autocreate = autocreate;

    def login_hint(self, request):
        if self.auth_hint:
            return request.getText(self.auth_hint)
        
        return None
        
    def login(self, request, user_obj, **kw):
        username = kw.get('username')
        password = kw.get('password')
        _ = request.getText

        if not username or not password:
            return ContinueLogin(user_obj)
    
        req = urllib2.Request(self.auth_url)
        
        try:
            handle = urllib2.urlopen(req)
        except IOError, e:
            # here we *want* to fail
            pass
        else:
            # If we don't fail then the page isn't protected
            return ContinueLogin(user_obj, _("Error Authenticating: URL used for RemoteHttpAuth "
                    " does not require authentication. Please contact administrator."))

        if not hasattr(e, 'code') or e.code != 401:
            # we got an error - but not a 401 error
            return ContinueLogin(user_obj, _("Error Authenticating: URL used for RemoteHttpAuth "
                    "does not require authentication or failed for antoher reason. "
                    "Please contact administrator."))

        authline = e.headers['www-authenticate']
        # this gets the www-authenticate line from the headers
        # which has the authentication scheme and realm in it

        authobj = re.compile(r'''(?:\s*www-authenticate\s*:)?\s*(\w*)\s+realm=['"]([^'"]+)['"]''', re.IGNORECASE)
        # this regular expression is used to extract scheme and realm
        matchobj = authobj.match(authline)

        if not matchobj:
            # if the authline isn't matched by the regular expression
            # then something is wrong
            return ContinueLogin(user_obj, _("Error Authenticating: Authentication header received from URL "
                    "used for RemoteHttpAuth is badly formed. Please contact administrator."))

        scheme = matchobj.group(1)
        realm = matchobj.group(2)
        # here we've extracted the scheme
        # and the realm from the header
        if scheme.lower() != 'basic':
            return ContinueLogin(user_obj, _("Error Authenticating: RemoteHttpAuth requires BASIC authentication "
                    "instead of '" + scheme + "'. Please contact administrator."))

        base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
        authheader =  "Basic %s" % base64string
        req.add_header("Authorization", authheader)
        
        try:
            handle = urllib2.urlopen(req)
        except IOError, e:
            # here we shouldn't fail if the username/password is right
            return ContinueLogin(user_obj, _("Incorrect username or password."))

        thepage = handle.read()

	if self.required_web_content:
	    if thepage != self.required_web_content:
		return ContinueLogin(user_obj, _("Authorization failed: '%s'" %(str(thepage))))
    
        u = user.User(request, name=username, auth_username=username, auth_method=self.name,
		auth_attribs=('name', 'auth_username', 'password'))

	if self.autocreate and self.auto_email_domain and not u.email:
	    u.email = username + "@" + self.auto_email_domain

	u.valid = True

        if u and self.autocreate:
            u.create_or_update(True)

        return ContinueLogin(u)
