"""
$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/dulcinea/lib/user.py $
$Id: user.py 31139 2008-09-17 10:58:49Z dbinger $
"""
from durus.utils import as_bytes
from qp.lib.spec import add_getters_and_setters, specify
from qp.lib.spec import spec, pattern, Specified
from qp.lib.util import randbytes, sha1
from qp.pub.common import get_publisher, get_users
from qp.pub.user import User
import binascii


def hash_password(password):
    """Apply a one way hash function to a password and return the result."""
    return sha1(as_bytes(password)).hexdigest()

def get_realm():
    return get_publisher().get_site().get_name()

class DulcineaUser (User, Specified):
    """
    a registered user.
    """
    global_permissions = {
        "act-as":
            "Allow to act as another user.",
        "create-users":
            "Allow the creation of other users.",
        "manage-permissions":
            "Allow changing of permissions.",
        "staff":
            ("Is a member of the staff, with all of the privileges and "
             "responsibilities thereunto appertaining."),
        "system":
            "Allow to do things normally done by the software system.",
        }

    id_is = spec(
        pattern('^[-A-Za-z0-9_@. ]*$'),
        "unique among users here, spaces allowed")

    def __str__(self):
        return self.id or '*no id*'

    format = __str__ # subclasses should override

    def get_key(self):
        """ used for forming component representing this user in URLs
        """
        return self.get_id()

    def set_id(self, user_id):
        assert self.id == None
        specify(self, id=user_id)

    def set_password(self, new_password, realm=None, check=True):
        """Set the user's password to 'new_password'."""
        if check and self.check_new_password(new_password) != "":
            raise ValueError('invalid password')
        User.set_password(self, new_password, realm=realm)

    def has_password(self, password, realm=None):
        """Return true if the provided password is correct."""
        digester = self.get_digester()
        if realm is None:
            realm = get_realm()
        digest = digester.get_digest(realm)
        if User.has_password(self, password, realm=realm):
            return True
        elif digest == hash_password(password):
            # If the digest uses the older Dulcinea hash, set
            # a new digest using the QP hash.
            User.set_password(self, password, realm=realm)
            return True
        else:
            return False

    def generate_password (self, realm=None, length=6):
        """Set the password to a random value and return the new password."""
        password = binascii.b2a_base64(binascii.unhexlify(randbytes(length)))
        password = password[:length]
        self.set_password(password, realm=realm)
        return password

    def check_new_password(self, new_password):
        """(string) -> string
        Check if a new password is valid.  Returns the empty string if the
        password is okay otherwise returns a string that describes what is
        wrong with the entered password.
        """
        return ""

    def format_realname(self):
        return ''

    def is_null(self):
        return not self

    def is_disabled(self, realm=None):
        return not bool(self.get_digester().get_digest(realm or get_realm()))

    def is_system(self):
        return self.id == 'SYSTEM'

    def is_admin(self):
        return self.is_granted('staff')

    def is_granted(self, permission, granter=True):
        return self.get_permissions().is_granted(permission, granter)

    def can_manage_permissions(self):
        return self.is_granted('manage-permissions')

    def get_email_realname_pair(self):
        return self.get_email(), self.format_realname() or ''

add_getters_and_setters(DulcineaUser)

def list_users():
    return [user for user in get_users().itervalues()
           if not (user.is_null() or
                   user.is_system() or
                   user.is_disabled())]

def gen_users_granted(permission, granter=True):
    for user in get_users().itervalues():
        if user.is_granted(permission, granter):
            yield user

def get_matching_user(identifier):
    """(identifier : string) -> DulcineaUser | None

    Return a user with matching id or email address, or None
    if no such user is found.
    """
    return lookup_user(id=identifier, email=identifier)

def add_user(user):
    return get_publisher().add_user(user)

def lookup_user(id=None, email=None, **kwargs):
    return get_publisher().lookup_user(id=id, email=email, **kwargs)

def text_format_user(user):
    if not user:
        return 'None'
    realname = user.format_realname()
    if realname:
        text = '%s (%s)' % (realname, user.get_id())
    else:
        text = user.get_id()
    email = user.get_email()
    if email:
        text += ' <%s>' % email
    return text