# -*- coding: iso-8859-1 -*-
"""
    MoinMoin - Authentication against xenforo

    if we have a cookie in the format: xf_session=bxxxx27f98162681a2b45150fe36065b
    then we look in the xf_session table for session_id = <cookie>.
    if the row doesn't exist, deny
    if that row exists, it will be like:
	session_id = bxxxx27f98162681a2b45150fe36065b
	session_data = a:7:{s:12:"sessionStart";i:1342373577;s:2:"ip";b:0;s:7:"user_id";i:1;s:16:"previousActivity";i:1342373565;s:16:"dismissedNotices";a:0:{}s:12:"reportCounts";a:3:{s:5:"total";i:0;s:8:"assigned";i:0;s:13:"lastBuildDate";i:1342373577;}s:16:"moderationCounts";a:2:{s:5:"total";i:0;s:13:"lastBuildDate";i:1342373577;}}
	expiry_date = 1342378332
    if now() > expiry_date then deny
    else we then parse out the user_id;i;1;s;16 bit.
    The user_id == 1 from above, we look for in xf_user table:
	user_id == 1
	username == donbowman

    @copyright: 2012 Don Bowman (don.waterloo@gmail.com)
    @license: GNU GPL, see COPYING for details.
"""

from MoinMoin import log
logging = log.getLogger(__name__)

import MySQLdb
import urllib, sys, re, time
import traceback
from MoinMoin import user
from MoinMoin.auth import BaseAuth

class XenforoAuth(BaseAuth):
    """ authenticate to Xenforo database from the xf_session cookie
    """
    name = 'xenforo_session'

    def __init__(self, autocreate=True):
        """ @param autocreate: if true then create the user on first run
        """
        BaseAuth.__init__(self)
        self.autocreate = autocreate

    def request(self, request, user_obj, **kw):
	username = kw.get('name')
	password = kw.get('password')
	login = kw.get('login')
	user_obj = kw.get('user_obj')
	cfg = request.cfg

        cookie = kw.get('cookie')
        if cookie is None:
	    logging.error("xenforo: no cookies")
	    return user_obj, True
	try:
	    session_id = cookie['xf_session']
	except:
	    info = sys.exc_info()
	    logging.error("xenforo: no xf_session cookie")
	    return user_obj, True

	# To avoid hammering the mysql, store in a session variable
	if (request.session.new == False):
	    if 'xf_username' in request.session:
		logging.info("use %s for username from session" % request.session['xf_username'])
		u = user.User(request,
		              name=request.session['xf_username'],
			      auth_username=request.session['xf_username'],
			      auth_method=self.name)
		if u and self.autocreate:
		    u.create_or_update(False)
		if u and u.valid:
		    return u, True # True to get other methods called, too

	try:
	    connection = MySQLdb.connect(host=cfg.auth_xenforo_mysql_host,
					 user=cfg.auth_xenforo_mysql_user,
					 passwd=cfg.auth_xenforo_mysql_pass,
					 db=cfg.auth_xenforo_mysql_db)
	    cursor = connection.cursor()
	    cursor.execute("SELECT session_id, session_data, expiry_date " +
	                   "FROM xf_session " +
			   "WHERE session_id = '" + session_id + "'")
	    data = cursor.fetchall()
	    if (len(data) != 0):
		# now check expiry as data[0][2]
		if (time.time() > data[0][2]):
		    logging.error("session has expired for %s" % session_id)
		else:
		    # OK, now we know the user is valid, has a valid session, lets
		    # get their name from xf_user after parsing the user_id from the string
		    # <i'm not really sure what the encoding is of this string>
		    partial = re.sub('.*"user_id";i:','',data[0][1],1)
		    user_id = re.sub(';.*','',partial)
		    cursor.execute("SELECT username " +
				   "FROM xf_user " +
				   "WHERE user_id = '" + user_id + "'")
		    data = cursor.fetchall()
		    if (len(data) != 1):
			logging.error("invalid user_id %s, got %d rows" % (user_id, len(data)))
		    else:
			username = data[0][0]
			request.session['xf_username'] = username
			logging.info("Username %s has logged in" % username)
			u = user.User(request, name=username, auth_username=username, auth_method=self.name)
			if u and self.autocreate:
			    u.create_or_update(False)
			if u and u.valid:
			    return u, True # True to get other methods called, too
	    else:
		logging.error("session %s doesn't exist in db" % session_id)
	except:
	    info = sys.exc_info()
	    logging.error("xenforo: caught an exception, traceback follows...")
	    logging.error(''.join(traceback.format_exception(*info)))

        return user_obj, True # continue with next method in auth list
