Example #1
0
    def get_collection(self, mbox_collection=True, mbox_name=None,
                       mbox_uuid=None):
        """
        Get a collection for tests.
        """
        adaptor = SoledadMailAdaptor()
        store = self._soledad
        adaptor.store = store

        if mbox_collection:
            mbox_indexer = MailboxIndexer(store)
            mbox_name = mbox_name or "TestMbox"
            mbox_uuid = mbox_uuid or str(uuid.uuid4())
        else:
            mbox_indexer = mbox_name = None

        def get_collection_from_mbox_wrapper(wrapper):
            wrapper.uuid = mbox_uuid
            return MessageCollection(
                adaptor, store,
                mbox_indexer=mbox_indexer, mbox_wrapper=wrapper)

        d = adaptor.initialize_store(store)
        if mbox_collection:
            d.addCallback(lambda _: mbox_indexer.create_table(mbox_uuid))
        d.addCallback(lambda _: adaptor.get_or_create_mbox(store, mbox_name))
        d.addCallback(get_collection_from_mbox_wrapper)
        return d
Example #2
0
    def get_lastuid(self, mbox):
        if isinstance(mbox, str):
            mbox = (yield defer.maybeDeferred(self.get_mbox, mbox))[0]

        indexer = MailboxIndexer(self.soledad)
        yield indexer.create_table(mbox.content['uuid'])
        last_uuid = yield indexer.get_last_uid(mbox.content['uuid'])

        defer.returnValue(last_uuid)
    def get_lastuid(self, mbox):
        if isinstance(mbox, str):
            mbox = (yield defer.maybeDeferred(self.get_mbox, mbox))[0]

        indexer = MailboxIndexer(self.soledad)
        yield indexer.create_table(mbox.content['uuid'])
        last_uuid = yield indexer.get_last_uid(mbox.content['uuid'])

        defer.returnValue(last_uuid)
Example #4
0
    def __init__(self, store, ready_cb=None):
        self.store = store
        self.adaptor = self.adaptor_class()

        # this is a mapping to collection instances so that we always
        # return a reference to them instead of creating new ones. however,
        # being a dictionary of weakrefs values, they automagically vanish
        # from the dict when no hard refs is left to them (so they can be
        # garbage collected) this is important because the different wrappers
        # rely on several kinds of deferredlocks that are kept as class or
        # instance variables
        self._collection_mapping = weakref.WeakValueDictionary()

        self.mbox_indexer = MailboxIndexer(self.store)

        # This flag is only used from the imap service for the moment.
        # In the future, we should prevent any public method to continue if
        # this is set to True. Also, it would be good to plug to the
        # authentication layer.
        self.session_ended = False

        self.deferred_initialization = defer.Deferred()
        self._ready_cb = ready_cb

        self._init_d = self._initialize_storage()
        self._initialize_sync_hooks()
Example #5
0
    def __init__(self, store, ready_cb=None):
        self.store = store
        self.adaptor = self.adaptor_class()
        self.mbox_indexer = MailboxIndexer(self.store)

        # This flag is only used from the imap service for the moment.
        # In the future, we should prevent any public method to continue if
        # this is set to True. Also, it would be good to plug to the
        # authentication layer.
        self.session_ended = False

        self.deferred_initialization = defer.Deferred()
        self._ready_cb = ready_cb

        self._init_d = self._initialize_storage()
        self._initialize_sync_hooks()
Example #6
0
class Account(object):
    """
    Account is the top level abstraction to access collections of messages
    associated with a LEAP Mail Account.

    It primarily handles creation and access of Mailboxes, which will be the
    basic collection handled by traditional MUAs, but it can also handle other
    types of Collections (tag based, for instance).

    leap.mail.imap.IMAPAccount partially proxies methods in this
    class.
    """

    # Adaptor is passed to the returned MessageCollections, so if you want to
    # use a different adaptor this is the place to change it, by subclassing
    # the Account class.

    adaptor_class = SoledadMailAdaptor

    def __init__(self, store, ready_cb=None):
        self.store = store
        self.adaptor = self.adaptor_class()

        # this is a mapping to collection instances so that we always
        # return a reference to them instead of creating new ones. however,
        # being a dictionary of weakrefs values, they automagically vanish
        # from the dict when no hard refs is left to them (so they can be
        # garbage collected) this is important because the different wrappers
        # rely on several kinds of deferredlocks that are kept as class or
        # instance variables
        self._collection_mapping = weakref.WeakValueDictionary()

        self.mbox_indexer = MailboxIndexer(self.store)

        # This flag is only used from the imap service for the moment.
        # In the future, we should prevent any public method to continue if
        # this is set to True. Also, it would be good to plug to the
        # authentication layer.
        self.session_ended = False

        self.deferred_initialization = defer.Deferred()
        self._ready_cb = ready_cb

        self._init_d = self._initialize_storage()
        self._initialize_sync_hooks()

    def _initialize_storage(self):

        def add_mailbox_if_none(mboxes):
            if not mboxes:
                return self.add_mailbox(INBOX_NAME)

        def finish_initialization(result):
            self.deferred_initialization.callback(None)
            if self._ready_cb is not None:
                self._ready_cb()

        d = self.adaptor.initialize_store(self.store)
        d.addCallback(lambda _: self.list_all_mailbox_names())
        d.addCallback(add_mailbox_if_none)
        d.addCallback(finish_initialization)
        return d

    def callWhenReady(self, cb, *args, **kw):
        """
        Execute the callback when the initialization of the Account is ready.
        Note that the callback will receive a first meaningless parameter.
        """
        # TODO this should ignore the first parameter explicitely
        # lambda _: cb(*args, **kw)
        self.deferred_initialization.addCallback(cb, *args, **kw)
        return self.deferred_initialization

    # Sync hooks

    def _initialize_sync_hooks(self):
        soledad_sync_hooks.post_sync_uid_reindexer.set_account(self)

    def _teardown_sync_hooks(self):
        soledad_sync_hooks.post_sync_uid_reindexer.set_account(None)

    #
    # Public API Starts
    #

    def list_all_mailbox_names(self):

        def filter_names(mboxes):
            return [m.mbox for m in mboxes]

        d = self.get_all_mailboxes()
        d.addCallback(filter_names)
        return d

    def get_all_mailboxes(self):
        d = self.adaptor.get_all_mboxes(self.store)
        return d

    def add_mailbox(self, name, creation_ts=None):

        if creation_ts is None:
            # by default, we pass an int value
            # taken from the current time
            # we make sure to take enough decimals to get a unique
            # mailbox-uidvalidity.
            creation_ts = int(time.time() * 10E2)

        def set_creation_ts(wrapper):
            wrapper.created = creation_ts
            d = wrapper.update(self.store)
            d.addCallback(lambda _: wrapper)
            return d

        def create_uuid(wrapper):
            if not wrapper.uuid:
                wrapper.uuid = str(uuid.uuid4())
                d = wrapper.update(self.store)
                d.addCallback(lambda _: wrapper)
                return d
            return wrapper

        def create_uid_table_cb(wrapper):
            d = self.mbox_indexer.create_table(wrapper.uuid)
            d.addCallback(lambda _: wrapper)
            return d

        d = self.adaptor.get_or_create_mbox(self.store, name)
        d.addCallback(set_creation_ts)
        d.addCallback(create_uuid)
        d.addCallback(create_uid_table_cb)
        return d

    def delete_mailbox(self, name):

        def delete_uid_table_cb(wrapper):
            d = self.mbox_indexer.delete_table(wrapper.uuid)
            d.addCallback(lambda _: wrapper)
            return d

        d = self.adaptor.get_or_create_mbox(self.store, name)
        d.addCallback(delete_uid_table_cb)
        d.addCallback(
            lambda wrapper: self.adaptor.delete_mbox(self.store, wrapper))
        return d

    def rename_mailbox(self, oldname, newname):

        def _rename_mbox(wrapper):
            wrapper.mbox = newname
            d = wrapper.update(self.store)
            d.addCallback(lambda result: wrapper)
            return d

        d = self.adaptor.get_or_create_mbox(self.store, oldname)
        d.addCallback(_rename_mbox)
        return d

    # Get Collections

    def get_collection_by_mailbox(self, name):
        """
        :rtype: deferred
        :return: a deferred that will fire with a MessageCollection
        """
        collection = self._collection_mapping.get(name, None)
        if collection:
            return defer.succeed(collection)

        # imap select will use this, passing the collection to SoledadMailbox
        def get_collection_for_mailbox(mbox_wrapper):
            collection = MessageCollection(
                self.adaptor, self.store, self.mbox_indexer, mbox_wrapper)
            self._collection_mapping[name] = collection
            return collection

        d = self.adaptor.get_or_create_mbox(self.store, name)
        d.addCallback(get_collection_for_mailbox)
        return d

    def get_collection_by_docs(self, docs):
        """
        :rtype: MessageCollection
        """
        # get a collection of docs by a list of doc_id
        # get.docs(...) --> it should be a generator. does it behave in the
        # threadpool?
        raise NotImplementedError()

    def get_collection_by_tag(self, tag):
        """
        :rtype: MessageCollection
        """
        raise NotImplementedError()

    # Session handling

    def end_session(self):
        self._teardown_sync_hooks()
        self.session_ended = True
Example #7
0
class Account(object):
    """
    Account is the top level abstraction to access collections of messages
    associated with a LEAP Mail Account.

    It primarily handles creation and access of Mailboxes, which will be the
    basic collection handled by traditional MUAs, but it can also handle other
    types of Collections (tag based, for instance).

    leap.mail.imap.IMAPAccount partially proxies methods in this
    class.
    """

    # Adaptor is passed to the returned MessageCollections, so if you want to
    # use a different adaptor this is the place to change it, by subclassing
    # the Account class.

    adaptor_class = SoledadMailAdaptor

    # This is a mapping to collection instances so that we always
    # return a reference to them instead of creating new ones. However, being a
    # dictionary of weakrefs values, they automagically vanish from the dict
    # when no hard refs is left to them (so they can be garbage collected)
    # This is important because the different wrappers rely on several
    # kinds of deferredLocks that are kept as class or instance variables
    _collection_mapping = weakref.WeakValueDictionary()

    def __init__(self, store, ready_cb=None):
        self.store = store
        self.adaptor = self.adaptor_class()
        self.mbox_indexer = MailboxIndexer(self.store)

        # This flag is only used from the imap service for the moment.
        # In the future, we should prevent any public method to continue if
        # this is set to True. Also, it would be good to plug to the
        # authentication layer.
        self.session_ended = False

        self.deferred_initialization = defer.Deferred()
        self._ready_cb = ready_cb

        self._init_d = self._initialize_storage()
        self._initialize_sync_hooks()

    def _initialize_storage(self):

        def add_mailbox_if_none(mboxes):
            if not mboxes:
                return self.add_mailbox(INBOX_NAME)

        def finish_initialization(result):
            self.deferred_initialization.callback(None)
            if self._ready_cb is not None:
                self._ready_cb()

        d = self.adaptor.initialize_store(self.store)
        d.addCallback(lambda _: self.list_all_mailbox_names())
        d.addCallback(add_mailbox_if_none)
        d.addCallback(finish_initialization)
        return d

    def callWhenReady(self, cb, *args, **kw):
        """
        Execute the callback when the initialization of the Account is ready.
        Note that the callback will receive a first meaningless parameter.
        """
        # TODO this should ignore the first parameter explicitely
        # lambda _: cb(*args, **kw)
        self.deferred_initialization.addCallback(cb, *args, **kw)
        return self.deferred_initialization

    # Sync hooks

    def _initialize_sync_hooks(self):
        soledad_sync_hooks.post_sync_uid_reindexer.set_account(self)

    def _teardown_sync_hooks(self):
        soledad_sync_hooks.post_sync_uid_reindexer.set_account(None)

    #
    # Public API Starts
    #

    def list_all_mailbox_names(self):

        def filter_names(mboxes):
            return [m.mbox for m in mboxes]

        d = self.get_all_mailboxes()
        d.addCallback(filter_names)
        return d

    def get_all_mailboxes(self):
        d = self.adaptor.get_all_mboxes(self.store)
        return d

    def add_mailbox(self, name, creation_ts=None):

        if creation_ts is None:
            # by default, we pass an int value
            # taken from the current time
            # we make sure to take enough decimals to get a unique
            # mailbox-uidvalidity.
            creation_ts = int(time.time() * 10E2)

        def set_creation_ts(wrapper):
            wrapper.created = creation_ts
            d = wrapper.update(self.store)
            d.addCallback(lambda _: wrapper)
            return d

        def create_uuid(wrapper):
            if not wrapper.uuid:
                wrapper.uuid = str(uuid.uuid4())
                d = wrapper.update(self.store)
                d.addCallback(lambda _: wrapper)
                return d
            return wrapper

        def create_uid_table_cb(wrapper):
            d = self.mbox_indexer.create_table(wrapper.uuid)
            d.addCallback(lambda _: wrapper)
            return d

        d = self.adaptor.get_or_create_mbox(self.store, name)
        d.addCallback(set_creation_ts)
        d.addCallback(create_uuid)
        d.addCallback(create_uid_table_cb)
        return d

    def delete_mailbox(self, name):

        def delete_uid_table_cb(wrapper):
            d = self.mbox_indexer.delete_table(wrapper.uuid)
            d.addCallback(lambda _: wrapper)
            return d

        d = self.adaptor.get_or_create_mbox(self.store, name)
        d.addCallback(delete_uid_table_cb)
        d.addCallback(
            lambda wrapper: self.adaptor.delete_mbox(self.store, wrapper))
        return d

    def rename_mailbox(self, oldname, newname):

        def _rename_mbox(wrapper):
            wrapper.mbox = newname
            d = wrapper.update(self.store)
            d.addCallback(lambda result: wrapper)
            return d

        d = self.adaptor.get_or_create_mbox(self.store, oldname)
        d.addCallback(_rename_mbox)
        return d

    # Get Collections

    def get_collection_by_mailbox(self, name):
        """
        :rtype: deferred
        :return: a deferred that will fire with a MessageCollection
        """
        collection = self._collection_mapping.get(name, None)
        if collection:
            return defer.succeed(collection)

        # imap select will use this, passing the collection to SoledadMailbox
        def get_collection_for_mailbox(mbox_wrapper):
            collection = MessageCollection(
                self.adaptor, self.store, self.mbox_indexer, mbox_wrapper)
            self._collection_mapping[name] = collection
            return collection

        d = self.adaptor.get_or_create_mbox(self.store, name)
        d.addCallback(get_collection_for_mailbox)
        return d

    def get_collection_by_docs(self, docs):
        """
        :rtype: MessageCollection
        """
        # get a collection of docs by a list of doc_id
        # get.docs(...) --> it should be a generator. does it behave in the
        # threadpool?
        raise NotImplementedError()

    def get_collection_by_tag(self, tag):
        """
        :rtype: MessageCollection
        """
        raise NotImplementedError()

    # Session handling

    def end_session(self):
        self._teardown_sync_hooks()
        self.session_ended = True