# Copyright (C) 2008 by Intevation GmbH
# Authors:
# Bernhard Herzog <bh@intevation.de>
#
# This program is free software under the LGPL (>=v2.1)
# Read the file COPYING coming with the software for details.

import os
import imaplib
import ConfigParser

def convert_bool(s):
    s = s.lower()
    if s in ("true", "yes", "1"):
        return True
    if s in ("false", "no", "0"):
        return False
    raise ValueError("cannot determine boolean value of %r" % (s,))


class ImapConfig(object):

    def __init__(self, configfile):
        parser = ConfigParser.SafeConfigParser()
        parser.read([configfile])
        for name, conv in [("host", str), ("port", int),
                           ("login", str), ("password", str),
                           ("ssl", convert_bool)]:
            setattr(self, name, conv(parser.get("imap", name)))


default_config_file = os.path.join(os.path.dirname(__file__), "imaptest.conf")

def default_config():
    return ImapConfig(default_config_file)


class AnnotationsMixin:

    def install_commands_in_imaplib(self):
        imaplib.Commands['GETANNOTATION'] = ('AUTH', 'SELECTED')
        imaplib.Commands['SETANNOTATION'] = ('AUTH', 'SELECTED')

    def to_argument_string(self, arg):
        if isinstance(arg, basestring):
            return self._quote(arg)
        elif arg is None:
            return "NIL"
        else:
            return "(%s)" % (" ".join(self.to_argument_string(item)
                                      for item in arg),)

    def getannotation(self, *args):
        # normally we would implement this command like the other
        # commands in IMAP with an explicit list of parameters.  For
        # tests it's useful to be able to omit parameters or to send too
        # many, so we allow an arbitrary number of parameters
        self.install_commands_in_imaplib()
        args = tuple(self.to_argument_string(item) for item in args)
        typ, dat = self._simple_command('GETANNOTATION', *args)
        return self._untagged_response(typ, dat, 'ANNOTATION')

    def setannotation(self, *args):
        # normally we would implement this command like the other
        # commands in IMAP with an explicit list of parameters.  For
        # tests it's useful to be able to omit parameters or to send too
        # many, so we allow an arbitrary number of parameters
        self.install_commands_in_imaplib()
        args = tuple(self.to_argument_string(item) for item in args)
        return self._simple_command('SETANNOTATION', *args)


class IMAP4_annotations(imaplib.IMAP4, AnnotationsMixin):

    pass


class IMAP4_SSL_annotations(imaplib.IMAP4_SSL, AnnotationsMixin):

    pass



class ImapError(Exception):

    def __init__(self, host, port, imap_result):
        Exception.__init__(self, "Unexpected response from imap server"
                           " (%r:%d):%r" % (host, port, imap_result))
        self.imap_result = imap_result


class ImapConnection(object):

    def __init__(self, host, port, login, password, use_ssl):
        self.host = host
        self.port = port
        if use_ssl:
            cls = IMAP4_SSL_annotations
        else:
            cls = IMAP4_annotations
        self.error_class = cls.error
        self.imap = cls(host, port)
        self.auth_plain(login, password)

    def check_res(self, res, ok_responses=("OK",)):
        assert len(res) == 2
        if res[0] not in ok_responses:
            raise ImapError(self.host, self.port, res)
        data = res[1]
        # for some reason imaplib uses a one-element list containing
        # None to indicate that no untagged responses came from the
        # server.  Translate this to an empty list.
        if data == [None]:
            data = []
        return data

    def auth_plain(self, login, password):
        self.check_res(self.imap.login(login, password))

    def logout(self):
        self.check_res(self.imap.logout(), ok_responses=("OK", "BYE"))

    @property
    def capabilities(self):
        return self.imap.capabilities

    def create(self, mailbox):
        self.check_res(self.imap.create(mailbox))

    def delete(self, mailbox):
        self.check_res(self.imap.delete(mailbox))

    def rename(self, oldmailbox, newmailbox):
        self.check_res(self.imap.rename(oldmailbox, newmailbox))

    def getannotation(self, *args):
        return self.check_res(self.imap.getannotation(*args))

    def setannotation(self, *args):
        return self.check_res(self.imap.setannotation(*args))
