diff options
| -rw-r--r-- | mail/src/leap/mail/imap/mailbox.py | 11 | ||||
| -rw-r--r-- | mail/src/leap/mail/imap/messages.py | 250 | ||||
| -rw-r--r-- | mail/src/leap/mail/imap/tests/test_imap.py | 130 | 
3 files changed, 7 insertions, 384 deletions
| diff --git a/mail/src/leap/mail/imap/mailbox.py b/mail/src/leap/mail/imap/mailbox.py index 2653ae4..91c6549 100644 --- a/mail/src/leap/mail/imap/mailbox.py +++ b/mail/src/leap/mail/imap/mailbox.py @@ -82,9 +82,9 @@ class IMAPMailbox(object):      A Soledad-backed IMAP mailbox.      Implements the high-level method needed for the Mailbox interfaces. -    The low-level database methods are contained in IMAPMessageCollection -    class, which we instantiate and make accessible in the `messages` -    attribute. +    The low-level database methods are contained in the generic +    MessageCollection class. We receive an instance of it and it is made +    accessible in the `collection` attribute.      """      implements(          imap4.IMailbox, @@ -107,14 +107,13 @@ class IMAPMailbox(object):      def __init__(self, collection, rw=1):          """ -        :param collection: instance of IMAPMessageCollection -        :type collection: IMAPMessageCollection +        :param collection: instance of MessageCollection +        :type collection: MessageCollection          :param rw: read-and-write flag for this mailbox          :type rw: int          """          self.rw = rw -          self._uidvalidity = None          self.collection = collection diff --git a/mail/src/leap/mail/imap/messages.py b/mail/src/leap/mail/imap/messages.py index b7bb6ee..02aac2e 100644 --- a/mail/src/leap/mail/imap/messages.py +++ b/mail/src/leap/mail/imap/messages.py @@ -15,23 +15,20 @@  # You should have received a copy of the GNU General Public License  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  """ -IMAPMessage and IMAPMessageCollection. +IMAPMessage implementation.  """  import logging  from twisted.mail import imap4  from twisted.internet import defer  from zope.interface import implements -from leap.common.check import leap_assert, leap_assert_type  from leap.mail.utils import find_charset  logger = logging.getLogger(__name__) -# TODO ------------------------------------------------------------ - +# TODO  # [ ] Add ref to incoming message during add_msg. -# [ ] Delete incoming mail only after successful write.  class IMAPMessage(object): @@ -251,246 +248,3 @@ def _format_headers(headers, negate, *names):          if cond(key):              _headers[key] = value      return _headers - - -class IMAPMessageCollection(object): -    """ -    A collection of messages, surprisingly. - -    It is tied to a selected mailbox name that is passed to its constructor. -    Implements a filter query over the messages contained in a soledad -    database. -    """ - -    messageklass = IMAPMessage - -    # TODO -    # [ ] Add RECENT flags docs to mailbox-doc attributes (list-of-uids) -    # [ ] move Query for all the headers documents to Collection - -    # TODO this should be able to produce a MessageSet methinks -    # TODO --- reimplement, review and prune documentation below. - -    FLAGS_DOC = "FLAGS" -    HEADERS_DOC = "HEADERS" -    CONTENT_DOC = "CONTENT" -    """ -    RECENT_DOC is a document that stores a list of the UIDs -    with the recent flag for this mailbox. It deserves a special treatment -    because: -    (1) it cannot be set by the user -    (2) it's a flag that we set inmediately after a fetch, which is quite -        often. -    (3) we need to be able to set/unset it in batches without doing a single -        write for each element in the sequence. -    """ -    RECENT_DOC = "RECENT" -    """ -    HDOCS_SET_DOC is a document that stores a set of the Document-IDs -    (the u1db index) for all the headers documents for a given mailbox. -    We use it to prefetch massively all the headers for a mailbox. -    This is the second massive query, after fetching all the FLAGS,  that -    a typical IMAP MUA will do in a case where we do not have local disk cache. -    """ -    HDOCS_SET_DOC = "HDOCS_SET" - -    def __init__(self, collection): -        """ -        Constructor for IMAPMessageCollection. - -        :param collection: an instance of a MessageCollection -        :type collection: MessageCollection -        """ -        leap_assert( -            collection.is_mailbox_collection(), -            "Need a mailbox name to initialize") -        mbox_name = collection.mbox_name -        leap_assert(mbox_name.strip() != "", "mbox cannot be blank space") -        leap_assert(isinstance(mbox_name, (str, unicode)), -                    "mbox needs to be a string") -        self.collection = collection - -        # XXX this has to be done in IMAPAccount -        # (Where the collection must be instantiated and passed to us) -        # self.mbox = normalize_mailbox(mbox) - -    @property -    def mbox_name(self): -        """ -        Return the string that identifies this mailbox. -        """ -        return self.collection.mbox_name - -    def add_msg(self, raw, flags=None, date=None): -        """ -        Creates a new message document. - -        :param raw: the raw message -        :type raw: str - -        :param flags: flags -        :type flags: list - -        :param date: the received date for the message -        :type date: str - -        :return: a deferred that will be fired with the message -                 uid when the adding succeed. -        :rtype: deferred -        """ -        if flags is None: -            flags = tuple() -        leap_assert_type(flags, tuple) -        return self.collection.add_msg(raw, flags, date) - -    def get_msg_by_uid(self, uid, absolute=True): -        """ -        Retrieves a IMAPMessage by UID. -        This is used primarity in the Mailbox fetch and store methods. - -        :param uid: the message uid to query by -        :type uid: int - -        :rtype: IMAPMessage -        """ -        def make_imap_msg(msg): -            kls = self.messageklass -            # TODO --- remove ref to collection -            return kls(msg, self.collection) - -        d = self.collection.get_msg_by_uid(uid, absolute=absolute) -        d.addCalback(make_imap_msg) -        return d - - -    # TODO -- move this to collection too -    # Used for the Search (Drafts) queries? -    def _get_uid_from_msgid(self, msgid): -        """ -        Return a UID for a given message-id. - -        It first gets the headers-doc for that msg-id, and -        it found it queries the flags doc for the current mailbox -        for the matching content-hash. - -        :return: A UID, or None -        """ -        return self._get_uid_from_msgidCb(msgid) - -    # TODO handle deferreds -    def set_flags(self, messages, flags, mode): -        """ -        Set flags for a sequence of messages. - -        :param mbox: the mbox this message belongs to -        :type mbox: str or unicode -        :param messages: the messages to iterate through -        :type messages: sequence -        :flags: the flags to be set -        :type flags: tuple -        :param mode: the mode for setting. 1 is append, -1 is remove, 0 set. -        :type mode: int -        :param observer: a deferred that will be called with the dictionary -                         mapping UIDs to flags after the operation has been -                         done. -        :type observer: deferred -        """ -        getmsg = self.get_msg_by_uid - -        def set_flags(uid, flags, mode): -            msg = getmsg(uid) -            if msg is not None: -                # XXX IMAPMessage needs access to the collection -                # to be able to set flags. Better if we make use -                # of collection... here. -                return uid, msg.setFlags(flags, mode) - -        setted_flags = [set_flags(uid, flags, mode) for uid in messages] -        result = dict(filter(None, setted_flags)) -        # XXX return gatherResults or something -        return result - -    def count(self): -        """ -        Return the count of messages for this mailbox. - -        :rtype: int -        """ -        return self.collection.count() - -    # headers query - -    def all_headers(self): -        """ -        Return a dict with all the header documents for this -        mailbox. - -        :rtype: dict -        """ -        # Use self.collection.mbox_indexer -        # and derive all the doc_ids for the hdocs -        raise NotImplementedError() - -    # unseen messages - -    def unseen_iter(self): -        """ -        Get an iterator for the message UIDs with no `seen` flag -        for this mailbox. - -        :return: iterator through unseen message doc UIDs -        :rtype: iterable -        """ -        raise NotImplementedError() - -    def count_unseen(self): -        """ -        Count all messages with the `Unseen` flag. - -        :returns: count -        :rtype: int -        """ -        return len(list(self.unseen_iter())) - -    def get_unseen(self): -        """ -        Get all messages with the `Unseen` flag - -        :returns: a list of LeapMessages -        :rtype: list -        """ -        raise NotImplementedError() -        #return [self.messageklass(self._soledad, doc_id, self.mbox) -                #for doc_id in self.unseen_iter()] - -    # recent messages - -    def count_recent(self): -        """ -        Count all messages with the `Recent` flag. -        It just retrieves the length of the recent_flags set, -        which is stored in a specific type of document for -        this collection. - -        :returns: count -        :rtype: int -        """ -        raise NotImplementedError() - -    # magic - -    def __len__(self): -        """ -        Returns the number of messages on this mailbox. -        :rtype: int -        """ -        return self.count() - -    def __repr__(self): -        """ -        Representation string for this object. -        """ -        return u"<IMAPMessageCollection: mbox '%s' (%s)>" % ( -            self.mbox_name, self.count()) - -    # TODO implement __iter__ ? diff --git a/mail/src/leap/mail/imap/tests/test_imap.py b/mail/src/leap/mail/imap/tests/test_imap.py index 802bc9d..fbe02d4 100644 --- a/mail/src/leap/mail/imap/tests/test_imap.py +++ b/mail/src/leap/mail/imap/tests/test_imap.py @@ -38,7 +38,6 @@ from twisted.python import failure  from twisted import cred  from leap.mail.imap.mailbox import IMAPMailbox -from leap.mail.imap.messages import IMAPMessageCollection  from leap.mail.imap.tests.utils import IMAP4HelperMixin @@ -70,139 +69,10 @@ class TestRealm:      def requestAvatar(self, avatarId, mind, *interfaces):          return imap4.IAccount, self.theAccount, lambda: None -  #  # TestCases  # -# TODO rename to IMAPMessageCollection -class MessageCollectionTestCase(IMAP4HelperMixin): -    """ -    Tests for the MessageCollection class -    """ -    count = 0 - -    def setUp(self): -        """ -        setUp method for each test -        We override mixin method since we are only testing -        MessageCollection interface in this particular TestCase -        """ -        # FIXME -- return deferred -        super(MessageCollectionTestCase, self).setUp() - -        # FIXME --- update initialization -        self.messages = IMAPMessageCollection( -            "testmbox%s" % (self.count,), self._soledad) -        MessageCollectionTestCase.count += 1 - -    def tearDown(self): -        """ -        tearDown method for each test -        """ -        del self.messages - -    def testEmptyMessage(self): -        """ -        Test empty message and collection -        """ -        em = self.messages._get_empty_doc() -        self.assertEqual( -            em, -            { -                "chash": '', -                "deleted": False, -                "flags": [], -                "mbox": "inbox", -                "seen": False, -                "multi": False, -                "size": 0, -                "type": "flags", -                "uid": 1, -            }) -        self.assertEqual(self.messages.count(), 0) - -    def testMultipleAdd(self): -        """ -        Add multiple messages -        """ -        mc = self.messages -        self.assertEqual(self.messages.count(), 0) - -        def add_first(): -            d = defer.gatherResults([ -                mc.add_msg('Stuff 1', subject="test1"), -                mc.add_msg('Stuff 2', subject="test2"), -                mc.add_msg('Stuff 3', subject="test3"), -                mc.add_msg('Stuff 4', subject="test4")]) -            return d - -        def add_second(result): -            d = defer.gatherResults([ -                mc.add_msg('Stuff 5', subject="test5"), -                mc.add_msg('Stuff 6', subject="test6"), -                mc.add_msg('Stuff 7', subject="test7")]) -            return d - -        def check_second(result): -            return self.assertEqual(mc.count(), 7) - -        d1 = add_first() -        d1.addCallback(add_second) -        d1.addCallback(check_second) - -    def testRecentCount(self): -        """ -        Test the recent count -        """ -        mc = self.messages -        countrecent = mc.count_recent -        eq = self.assertEqual - -        self.assertEqual(countrecent(), 0) - -        d = mc.add_msg('Stuff', subject="test1") -        # For the semantics defined in the RFC, we auto-add the -        # recent flag by default. - -        def add2(_): -            return mc.add_msg('Stuff', subject="test2", -                              flags=('\\Deleted',)) - -        def add3(_): -            return mc.add_msg('Stuff', subject="test3", -                              flags=('\\Recent',)) - -        def add4(_): -            return mc.add_msg('Stuff', subject="test4", -                              flags=('\\Deleted', '\\Recent')) - -        d.addCallback(lambda r: eq(countrecent(), 1)) -        d.addCallback(add2) -        d.addCallback(lambda r: eq(countrecent(), 2)) -        d.addCallback(add3) -        d.addCallback(lambda r: eq(countrecent(), 3)) -        d.addCallback(add4) -        d.addCallback(lambda r: eq(countrecent(), 4)) - -    def testFilterByMailbox(self): -        """ -        Test that queries filter by selected mailbox -        """ -        mc = self.messages -        self.assertEqual(self.messages.count(), 0) - -        def add_1(): -            d1 = mc.add_msg('msg 1', subject="test1") -            d2 = mc.add_msg('msg 2', subject="test2") -            d3 = mc.add_msg('msg 3', subject="test3") -            d = defer.gatherResults([d1, d2, d3]) -            return d - -        add_1().addCallback(lambda ignored: self.assertEqual( -                            mc.count(), 3)) - -  # DEBUG ---  #from twisted.internet.base import DelayedCall  #DelayedCall.debug = True | 
