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
def __init__(self, store, user_id, ready_cb=None): self.store = store self.user_id = user_id 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 getCollection(_): adaptor = SoledadMailAdaptor() store = self._soledad adaptor.store = store mbox_indexer = MailboxIndexer(store) mbox_name = "INBOX" mbox_uuid = str(uuid.uuid4()) 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) 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
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.bitmask.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 defaultdict, indexed by userid, that returns a # WeakValueDictionary 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. # We need it to be a class property because we create more than one Account # object in the current usage pattern (ie, one in the mail service, and # another one in the IncomingMailService). When we move to a proper service # tree we can let it be an instance attribute. _collection_mapping = defaultdict(weakref.WeakValueDictionary) def __init__(self, store, user_id, ready_cb=None): self.store = store self.user_id = user_id 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() @defer.inlineCallbacks def _initialize_storage(self): yield self.adaptor.initialize_store(self.store) mboxes = yield self.list_all_mailbox_names() if INBOX_NAME not in mboxes: yield self.add_mailbox(INBOX_NAME) # This is so that we create the mboxes before Pixelated tries # to do it. if 'Sent' not in mboxes: yield self.add_mailbox('Sent') self.deferred_initialization.callback(None) if self._ready_cb is not None: self._ready_cb() 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. """ 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): return self.adaptor.atomic.run(self._add_mailbox, name, creation_ts=creation_ts) 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): return self.adaptor.atomic.run(self._delete_mailbox, name) 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): return self.adaptor.atomic.run(self._rename_mailbox, oldname, newname) 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 """ return self.adaptor.atomic.run(self._get_collection_by_mailbox, name) def _get_collection_by_mailbox(self, name): collection = self._collection_mapping[self.user_id].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[self.user_id][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 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