# Copyright 2011 SPIELO International.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
#     1. Redistributions of source code must retain the above copyright notice, 
#        this list of conditions and the following disclaimer.
#  
#     2. Redistributions in binary form must reproduce the above copyright 
#        notice, this list of conditions and the following disclaimer in the
#        documentation and/or other materials provided with the distribution.
#
#     3. Neither the name of Django nor the names of its contributors may be used
#        to endorse or promote products derived from this software without
#        specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
#
# Revision history:
#
# 2011-10-16  Michael Foetsch  mfoetsch@spielo-int.com
#
#       * Initial version
#

import codecs
import logging
import os
import pickle
from django.contrib import auth
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist

class WikiAuthMiddleware(object):
    COOKIE_NAME = "MOIN_SESSION_443_ROOT"
    SESSION_DIR = "/wiki/data/cache/__session__"
    USER_DIR = "/wiki/data/user"

    def process_request(self, request):
        if not hasattr(request, 'user'):
            raise ImproperlyConfigured(
                "You need to add "
                " 'django.contrib.auth.middleware.AuthenticationMiddleware'"
                " before WikiAuthMiddleware to the MIDDLEWARE_CLASSES setting.")
        try:
            token = request.COOKIES[self.COOKIE_NAME]
            logging.getLogger('wiki_auth').debug('found token %r' % (token,))
        except KeyError:
            return

        session_file = os.path.join(self.SESSION_DIR, token)
        if not os.path.isfile(session_file):
            logging.getLogger('wiki_auth').debug('session_file %r does not exist' % (session_file,))
            return
        try:
            f = open(session_file, "rb")
        except OSError:
            logging.getLogger('wiki_auth').warning('cannot open session_file %r' % (session_file,))
	    return
        session_data = f.read(4096)
        f.close()
        try:
            session_dict = pickle.loads(session_data)
        except:
            logging.getLogger('wiki_auth').warning('cannot unpickle session_file %r' % (session_file,))
            return
        try:
            user_id = session_dict['user.id']
        except KeyError:
            logging.getLogger('wiki_auth').warning('session_dict does not contain "user.id"')
            return
        logging.getLogger('wiki_auth').debug('user.id = %r' % (user_id,))

        user_file = os.path.join(self.USER_DIR, user_id)
        try:
            f = codecs.open(user_file, "r", "utf-8")
        except OSError:
            logging.getLogger('wiki_auth').warning('user %r does not exist' % (user_id,))
            return
        user_name = None
        user_email = None
        user_alias = None
        for ln in f:
            if not '=' in ln:
                continue
            split_ln = ln.split('=')
            key = split_ln[0]
            value = '='.join(split_ln[1:]).strip()
            if key == 'name':
                user_name = value
            elif key == 'email':
                user_email = value
            elif key == 'aliasname':
                user_alias = value
        logging.getLogger('wiki_auth').debug('credentials: user_name = %r, user_email = %r, user_alias = %r' % (user_name, user_email, user_alias))

        if request.user.is_authenticated() and request.user.username == user_name:
            logging.getLogger('wiki_auth').debug('user %r already logged in' % (user_name,))
            return

        user = auth.authenticate(user_name=user_name, user_email=user_email, user_alias=user_alias)
        if user:
            logging.getLogger('wiki_auth').debug('user %r is valid, logging in' % (user_name,))
            request.user = user
            auth.login(request, user)

class WikiAuthBackend(ModelBackend):
    def authenticate(self, user_name=None, user_email=None, user_alias=None):
        if not user_name:
            return
        user = None
        user, created = User.objects.get_or_create(username=user_name)
        if created:
            logging.getLogger('wiki_auth').debug('created user %r' % (user_name,))
            user.set_unusable_password()

        if user_email:
            user.email = user_email
        if user_alias:
            # Try to get first/last name from alias. Doesn't work so
            # well for someone named "William Henry Mountbatten Windsor",
            # but should be good enough.
            split_name = user_alias.split()
            user.first_name = split_name[0]
            user.last_name = ' '.join(split_name[1:])
        else:
            user.first_name = user_name
        user.save()
        return user
