From 62b0cd6301b7097dfa2776b677ab3c7d27f60d7b Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 26 Dec 2013 14:10:14 -0400 Subject: Split the near-2k loc file into more handy modules. ...aaaand not a single fuck was given that day! --- src/leap/mail/imap/account.py | 426 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 426 insertions(+) create mode 100644 src/leap/mail/imap/account.py (limited to 'src/leap/mail/imap/account.py') diff --git a/src/leap/mail/imap/account.py b/src/leap/mail/imap/account.py new file mode 100644 index 0000000..fd861e7 --- /dev/null +++ b/src/leap/mail/imap/account.py @@ -0,0 +1,426 @@ +# -*- coding: utf-8 -*- +# account.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Soledad Backed Account. +""" +import copy +import time + +from twisted.mail import imap4 +from zope.interface import implements + +from leap.common.check import leap_assert, leap_assert_type +from leap.mail.imap.index import IndexedDB +from leap.mail.imap.fields import WithMsgFields +from leap.mail.imap.parser import MBoxParser +from leap.mail.imap.mailbox import SoledadMailbox +from leap.soledad.client import Soledad + + +####################################### +# Soledad Account +####################################### + + +class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): + """ + An implementation of IAccount and INamespacePresenteer + that is backed by Soledad Encrypted Documents. + """ + + implements(imap4.IAccount, imap4.INamespacePresenter) + + _soledad = None + selected = None + + def __init__(self, account_name, soledad=None): + """ + Creates a SoledadAccountIndex that keeps track of the mailboxes + and subscriptions handled by this account. + + :param acct_name: The name of the account (user id). + :type acct_name: str + + :param soledad: a Soledad instance. + :param soledad: Soledad + """ + leap_assert(soledad, "Need a soledad instance to initialize") + leap_assert_type(soledad, Soledad) + + # XXX SHOULD assert too that the name matches the user/uuid with which + # soledad has been initialized. + + self._account_name = self._parse_mailbox_name(account_name) + self._soledad = soledad + + self.initialize_db() + + # every user should have the right to an inbox folder + # at least, so let's make one! + + if not self.mailboxes: + self.addMailbox(self.INBOX_NAME) + + def _get_empty_mailbox(self): + """ + Returns an empty mailbox. + + :rtype: dict + """ + return copy.deepcopy(self.EMPTY_MBOX) + + def _get_mailbox_by_name(self, name): + """ + Return an mbox document by name. + + :param name: the name of the mailbox + :type name: str + + :rtype: SoledadDocument + """ + doc = self._soledad.get_from_index( + self.TYPE_MBOX_IDX, self.MBOX_KEY, + self._parse_mailbox_name(name)) + return doc[0] if doc else None + + @property + def mailboxes(self): + """ + A list of the current mailboxes for this account. + """ + return [doc.content[self.MBOX_KEY] + for doc in self._soledad.get_from_index( + self.TYPE_IDX, self.MBOX_KEY)] + + @property + def subscriptions(self): + """ + A list of the current subscriptions for this account. + """ + return [doc.content[self.MBOX_KEY] + for doc in self._soledad.get_from_index( + self.TYPE_SUBS_IDX, self.MBOX_KEY, '1')] + + def getMailbox(self, name): + """ + Returns a Mailbox with that name, without selecting it. + + :param name: name of the mailbox + :type name: str + + :returns: a a SoledadMailbox instance + :rtype: SoledadMailbox + """ + name = self._parse_mailbox_name(name) + + if name not in self.mailboxes: + raise imap4.MailboxException("No such mailbox") + + return SoledadMailbox(name, soledad=self._soledad) + + ## + ## IAccount + ## + + def addMailbox(self, name, creation_ts=None): + """ + Add a mailbox to the account. + + :param name: the name of the mailbox + :type name: str + + :param creation_ts: an optional creation timestamp to be used as + mailbox id. A timestamp will be used if no + one is provided. + :type creation_ts: int + + :returns: True if successful + :rtype: bool + """ + name = self._parse_mailbox_name(name) + + if name in self.mailboxes: + raise imap4.MailboxCollision, name + + if not creation_ts: + # 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) + + mbox = self._get_empty_mailbox() + mbox[self.MBOX_KEY] = name + mbox[self.CREATED_KEY] = creation_ts + + doc = self._soledad.create_doc(mbox) + return bool(doc) + + def create(self, pathspec): + """ + Create a new mailbox from the given hierarchical name. + + :param pathspec: The full hierarchical name of a new mailbox to create. + If any of the inferior hierarchical names to this one + do not exist, they are created as well. + :type pathspec: str + + :return: A true value if the creation succeeds. + :rtype: bool + + :raise MailboxException: Raised if this mailbox cannot be added. + """ + # TODO raise MailboxException + paths = filter( + None, + self._parse_mailbox_name(pathspec).split('/')) + for accum in range(1, len(paths)): + try: + self.addMailbox('/'.join(paths[:accum])) + except imap4.MailboxCollision: + pass + try: + self.addMailbox('/'.join(paths)) + except imap4.MailboxCollision: + if not pathspec.endswith('/'): + return False + return True + + def select(self, name, readwrite=1): + """ + Selects a mailbox. + + :param name: the mailbox to select + :type name: str + + :param readwrite: 1 for readwrite permissions. + :type readwrite: int + + :rtype: bool + """ + name = self._parse_mailbox_name(name) + + if name not in self.mailboxes: + return None + + self.selected = name + + return SoledadMailbox( + name, rw=readwrite, + soledad=self._soledad) + + def delete(self, name, force=False): + """ + Deletes a mailbox. + + Right now it does not purge the messages, but just removes the mailbox + name from the mailboxes list!!! + + :param name: the mailbox to be deleted + :type name: str + + :param force: if True, it will not check for noselect flag or inferior + names. use with care. + :type force: bool + """ + name = self._parse_mailbox_name(name) + + if not name in self.mailboxes: + raise imap4.MailboxException("No such mailbox") + + mbox = self.getMailbox(name) + + if force is False: + # See if this box is flagged \Noselect + # XXX use mbox.flags instead? + if self.NOSELECT_FLAG in mbox.getFlags(): + # Check for hierarchically inferior mailboxes with this one + # as part of their root. + for others in self.mailboxes: + if others != name and others.startswith(name): + raise imap4.MailboxException, ( + "Hierarchically inferior mailboxes " + "exist and \\Noselect is set") + mbox.destroy() + + # XXX FIXME --- not honoring the inferior names... + + # if there are no hierarchically inferior names, we will + # delete it from our ken. + #if self._inferiorNames(name) > 1: + # ??! -- can this be rite? + #self._index.removeMailbox(name) + + def rename(self, oldname, newname): + """ + Renames a mailbox. + + :param oldname: old name of the mailbox + :type oldname: str + + :param newname: new name of the mailbox + :type newname: str + """ + oldname = self._parse_mailbox_name(oldname) + newname = self._parse_mailbox_name(newname) + + if oldname not in self.mailboxes: + raise imap4.NoSuchMailbox, oldname + + inferiors = self._inferiorNames(oldname) + inferiors = [(o, o.replace(oldname, newname, 1)) for o in inferiors] + + for (old, new) in inferiors: + if new in self.mailboxes: + raise imap4.MailboxCollision, new + + for (old, new) in inferiors: + mbox = self._get_mailbox_by_name(old) + mbox.content[self.MBOX_KEY] = new + self._soledad.put_doc(mbox) + + # XXX ---- FIXME!!!! ------------------------------------ + # until here we just renamed the index... + # We have to rename also the occurrence of this + # mailbox on ALL the messages that are contained in it!!! + # ... we maybe could use a reference to the doc_id + # in each msg, instead of the "mbox" field in msgs + # ------------------------------------------------------- + + def _inferiorNames(self, name): + """ + Return hierarchically inferior mailboxes. + + :param name: name of the mailbox + :rtype: list + """ + # XXX use wildcard query instead + inferiors = [] + for infname in self.mailboxes: + if infname.startswith(name): + inferiors.append(infname) + return inferiors + + def isSubscribed(self, name): + """ + Returns True if user is subscribed to this mailbox. + + :param name: the mailbox to be checked. + :type name: str + + :rtype: bool + """ + mbox = self._get_mailbox_by_name(name) + return mbox.content.get('subscribed', False) + + def _set_subscription(self, name, value): + """ + Sets the subscription value for a given mailbox + + :param name: the mailbox + :type name: str + + :param value: the boolean value + :type value: bool + """ + # maybe we should store subscriptions in another + # document... + if not name in self.mailboxes: + self.addMailbox(name) + mbox = self._get_mailbox_by_name(name) + + if mbox: + mbox.content[self.SUBSCRIBED_KEY] = value + self._soledad.put_doc(mbox) + + def subscribe(self, name): + """ + Subscribe to this mailbox + + :param name: name of the mailbox + :type name: str + """ + name = self._parse_mailbox_name(name) + if name not in self.subscriptions: + self._set_subscription(name, True) + + def unsubscribe(self, name): + """ + Unsubscribe from this mailbox + + :param name: name of the mailbox + :type name: str + """ + name = self._parse_mailbox_name(name) + if name not in self.subscriptions: + raise imap4.MailboxException, "Not currently subscribed to " + name + self._set_subscription(name, False) + + def listMailboxes(self, ref, wildcard): + """ + List the mailboxes. + + from rfc 3501: + returns a subset of names from the complete set + of all names available to the client. Zero or more untagged LIST + replies are returned, containing the name attributes, hierarchy + delimiter, and name. + + :param ref: reference name + :type ref: str + + :param wildcard: mailbox name with possible wildcards + :type wildcard: str + """ + # XXX use wildcard in index query + ref = self._inferiorNames( + self._parse_mailbox_name(ref)) + wildcard = imap4.wildcardToRegexp(wildcard, '/') + return [(i, self.getMailbox(i)) for i in ref if wildcard.match(i)] + + ## + ## INamespacePresenter + ## + + def getPersonalNamespaces(self): + return [["", "/"]] + + def getSharedNamespaces(self): + return None + + def getOtherNamespaces(self): + return None + + # extra, for convenience + + def deleteAllMessages(self, iknowhatiamdoing=False): + """ + Deletes all messages from all mailboxes. + Danger! high voltage! + + :param iknowhatiamdoing: confirmation parameter, needs to be True + to proceed. + """ + if iknowhatiamdoing is True: + for mbox in self.mailboxes: + self.delete(mbox, force=True) + + def __repr__(self): + """ + Representation string for this object. + """ + return "" % self._account_name -- cgit v1.2.3 From f9a9a695526afea8eef4ccba8904fb215acc43dd Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 8 Jan 2014 23:29:18 -0400 Subject: add a flag to be able to close the session --- src/leap/mail/imap/account.py | 1 + 1 file changed, 1 insertion(+) (limited to 'src/leap/mail/imap/account.py') diff --git a/src/leap/mail/imap/account.py b/src/leap/mail/imap/account.py index fd861e7..8caafef 100644 --- a/src/leap/mail/imap/account.py +++ b/src/leap/mail/imap/account.py @@ -46,6 +46,7 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): _soledad = None selected = None + closed = False def __init__(self, account_name, soledad=None): """ -- cgit v1.2.3 From 4381131163161c00f00a3eb300041374aa06d370 Mon Sep 17 00:00:00 2001 From: drebs Date: Sat, 28 Dec 2013 20:09:03 -0200 Subject: Convert unicode to str when raising in IMAP server (#4830). --- src/leap/mail/imap/account.py | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'src/leap/mail/imap/account.py') diff --git a/src/leap/mail/imap/account.py b/src/leap/mail/imap/account.py index fd861e7..8f5b57b 100644 --- a/src/leap/mail/imap/account.py +++ b/src/leap/mail/imap/account.py @@ -36,6 +36,23 @@ from leap.soledad.client import Soledad ####################################### +def _unicode_as_str(text): + """ + Return some representation of C{text} as a str. + + This is here mainly because Twisted's exception methods are not able to + print unicode text. + + :param text: The text to convert. + :type text: unicode + + :return: A representation of C{text} as str. + :rtype: str + """ + # XXX is there a better str representation for unicode? + return repr(text) + + class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): """ An implementation of IAccount and INamespacePresenteer @@ -128,7 +145,8 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): name = self._parse_mailbox_name(name) if name not in self.mailboxes: - raise imap4.MailboxException("No such mailbox") + raise imap4.MailboxException("No such mailbox: %s" % + _unicode_as_str(name)) return SoledadMailbox(name, soledad=self._soledad) @@ -154,7 +172,7 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): name = self._parse_mailbox_name(name) if name in self.mailboxes: - raise imap4.MailboxCollision, name + raise imap4.MailboxCollision, _unicode_as_str(name) if not creation_ts: # by default, we pass an int value @@ -240,7 +258,8 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): name = self._parse_mailbox_name(name) if not name in self.mailboxes: - raise imap4.MailboxException("No such mailbox") + raise imap4.MailboxException("No such mailbox: %s" % + _unicode_as_str(name)) mbox = self.getMailbox(name) @@ -279,14 +298,14 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): newname = self._parse_mailbox_name(newname) if oldname not in self.mailboxes: - raise imap4.NoSuchMailbox, oldname + raise imap4.NoSuchMailbox, _unicode_as_str(oldname) inferiors = self._inferiorNames(oldname) inferiors = [(o, o.replace(oldname, newname, 1)) for o in inferiors] for (old, new) in inferiors: if new in self.mailboxes: - raise imap4.MailboxCollision, new + raise imap4.MailboxCollision, _unicode_as_str(new) for (old, new) in inferiors: mbox = self._get_mailbox_by_name(old) @@ -367,7 +386,8 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): """ name = self._parse_mailbox_name(name) if name not in self.subscriptions: - raise imap4.MailboxException, "Not currently subscribed to " + name + raise imap4.MailboxException, \ + "Not currently subscribed to %s" % _unicode_as_str(name) self._set_subscription(name, False) def listMailboxes(self, ref, wildcard): -- cgit v1.2.3 From 4de544034406e3331280bdc86d10b3304d90c1d9 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Thu, 9 Jan 2014 16:44:04 -0300 Subject: Remove unneded repr wrapper. Also use pep8 recommended raise format: raise Exception("message") # instead of: raise Exception, "message" --- src/leap/mail/imap/account.py | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) (limited to 'src/leap/mail/imap/account.py') diff --git a/src/leap/mail/imap/account.py b/src/leap/mail/imap/account.py index 8f5b57b..6b10583 100644 --- a/src/leap/mail/imap/account.py +++ b/src/leap/mail/imap/account.py @@ -36,23 +36,6 @@ from leap.soledad.client import Soledad ####################################### -def _unicode_as_str(text): - """ - Return some representation of C{text} as a str. - - This is here mainly because Twisted's exception methods are not able to - print unicode text. - - :param text: The text to convert. - :type text: unicode - - :return: A representation of C{text} as str. - :rtype: str - """ - # XXX is there a better str representation for unicode? - return repr(text) - - class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): """ An implementation of IAccount and INamespacePresenteer @@ -145,8 +128,7 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): name = self._parse_mailbox_name(name) if name not in self.mailboxes: - raise imap4.MailboxException("No such mailbox: %s" % - _unicode_as_str(name)) + raise imap4.MailboxException("No such mailbox: %r" % name) return SoledadMailbox(name, soledad=self._soledad) @@ -172,7 +154,7 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): name = self._parse_mailbox_name(name) if name in self.mailboxes: - raise imap4.MailboxCollision, _unicode_as_str(name) + raise imap4.MailboxCollision(repr(name)) if not creation_ts: # by default, we pass an int value @@ -258,8 +240,7 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): name = self._parse_mailbox_name(name) if not name in self.mailboxes: - raise imap4.MailboxException("No such mailbox: %s" % - _unicode_as_str(name)) + raise imap4.MailboxException("No such mailbox: %r" % name) mbox = self.getMailbox(name) @@ -298,14 +279,14 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): newname = self._parse_mailbox_name(newname) if oldname not in self.mailboxes: - raise imap4.NoSuchMailbox, _unicode_as_str(oldname) + raise imap4.NoSuchMailbox(repr(oldname)) inferiors = self._inferiorNames(oldname) inferiors = [(o, o.replace(oldname, newname, 1)) for o in inferiors] for (old, new) in inferiors: if new in self.mailboxes: - raise imap4.MailboxCollision, _unicode_as_str(new) + raise imap4.MailboxCollision(repr(new)) for (old, new) in inferiors: mbox = self._get_mailbox_by_name(old) @@ -386,8 +367,8 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): """ name = self._parse_mailbox_name(name) if name not in self.subscriptions: - raise imap4.MailboxException, \ - "Not currently subscribed to %s" % _unicode_as_str(name) + raise imap4.MailboxException( + "Not currently subscribed to %r" % name) self._set_subscription(name, False) def listMailboxes(self, ref, wildcard): -- cgit v1.2.3 From 4ae6ad57a0f80143e3ded867c1fdd2264804a775 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 21 Jan 2014 19:22:09 -0400 Subject: memory store for append/fetch/copy --- src/leap/mail/imap/account.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'src/leap/mail/imap/account.py') diff --git a/src/leap/mail/imap/account.py b/src/leap/mail/imap/account.py index ce83079..7641ea8 100644 --- a/src/leap/mail/imap/account.py +++ b/src/leap/mail/imap/account.py @@ -48,7 +48,7 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): selected = None closed = False - def __init__(self, account_name, soledad=None): + def __init__(self, account_name, soledad=None, memstore=None): """ Creates a SoledadAccountIndex that keeps track of the mailboxes and subscriptions handled by this account. @@ -57,7 +57,9 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): :type acct_name: str :param soledad: a Soledad instance. - :param soledad: Soledad + :type soledad: Soledad + :param memstore: a MemoryStore instance. + :type memstore: MemoryStore """ leap_assert(soledad, "Need a soledad instance to initialize") leap_assert_type(soledad, Soledad) @@ -67,6 +69,7 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): self._account_name = self._parse_mailbox_name(account_name) self._soledad = soledad + self._memstore = memstore self.initialize_db() @@ -131,7 +134,8 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): if name not in self.mailboxes: raise imap4.MailboxException("No such mailbox: %r" % name) - return SoledadMailbox(name, soledad=self._soledad) + return SoledadMailbox(name, soledad=self._soledad, + memstore=self._memstore) ## ## IAccount @@ -221,8 +225,7 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): self.selected = name return SoledadMailbox( - name, rw=readwrite, - soledad=self._soledad) + name, self._soledad, self._memstore, readwrite) def delete(self, name, force=False): """ -- cgit v1.2.3 From a7e0054b595822325f749b0b1df7d25cab4e6486 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 28 Jan 2014 18:39:59 -0400 Subject: docstring fixes Also some fixes for None comparisons. --- src/leap/mail/imap/account.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/leap/mail/imap/account.py') diff --git a/src/leap/mail/imap/account.py b/src/leap/mail/imap/account.py index 7641ea8..f985c04 100644 --- a/src/leap/mail/imap/account.py +++ b/src/leap/mail/imap/account.py @@ -48,7 +48,7 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): selected = None closed = False - def __init__(self, account_name, soledad=None, memstore=None): + def __init__(self, account_name, soledad, memstore=None): """ Creates a SoledadAccountIndex that keeps track of the mailboxes and subscriptions handled by this account. @@ -134,7 +134,7 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): if name not in self.mailboxes: raise imap4.MailboxException("No such mailbox: %r" % name) - return SoledadMailbox(name, soledad=self._soledad, + return SoledadMailbox(name, self._soledad, memstore=self._memstore) ## -- cgit v1.2.3 From ee0786c57d72aa8b8da76533f33c3dd65253a878 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 6 Feb 2014 18:11:20 -0400 Subject: long-due update to unittests! So we're safe under the green lights before further rewriting. :) --- src/leap/mail/imap/account.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/leap/mail/imap/account.py') diff --git a/src/leap/mail/imap/account.py b/src/leap/mail/imap/account.py index f985c04..04af3b1 100644 --- a/src/leap/mail/imap/account.py +++ b/src/leap/mail/imap/account.py @@ -36,6 +36,10 @@ from leap.soledad.client import Soledad ####################################### +# TODO change name to LeapIMAPAccount, since we're using +# the memstore. +# IndexedDB should also not be here anymore. + class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): """ An implementation of IAccount and INamespacePresenteer @@ -67,6 +71,8 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): # XXX SHOULD assert too that the name matches the user/uuid with which # soledad has been initialized. + # XXX ??? why is this parsing mailbox name??? it's account... + # userid? homogenize. self._account_name = self._parse_mailbox_name(account_name) self._soledad = soledad self._memstore = memstore -- cgit v1.2.3 From 7eba9d5badcd3ebbdb746e6598ce452a8d9b7649 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 13 Feb 2014 11:42:06 -0400 Subject: avoid hitting db on every select --- src/leap/mail/imap/account.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'src/leap/mail/imap/account.py') diff --git a/src/leap/mail/imap/account.py b/src/leap/mail/imap/account.py index 04af3b1..fd35698 100644 --- a/src/leap/mail/imap/account.py +++ b/src/leap/mail/imap/account.py @@ -18,6 +18,7 @@ Soledad Backed Account. """ import copy +import logging import time from twisted.mail import imap4 @@ -30,6 +31,8 @@ from leap.mail.imap.parser import MBoxParser from leap.mail.imap.mailbox import SoledadMailbox from leap.soledad.client import Soledad +logger = logging.getLogger(__name__) + ####################################### # Soledad Account @@ -77,10 +80,13 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): self._soledad = soledad self._memstore = memstore + self.__mailboxes = set([]) + self.initialize_db() # every user should have the right to an inbox folder # at least, so let's make one! + self._load_mailboxes() if not self.mailboxes: self.addMailbox(self.INBOX_NAME) @@ -112,9 +118,13 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): """ A list of the current mailboxes for this account. """ - return [doc.content[self.MBOX_KEY] - for doc in self._soledad.get_from_index( - self.TYPE_IDX, self.MBOX_KEY)] + return self.__mailboxes + + def _load_mailboxes(self): + self.__mailboxes.update( + [doc.content[self.MBOX_KEY] + for doc in self._soledad.get_from_index( + self.TYPE_IDX, self.MBOX_KEY)]) @property def subscriptions(self): @@ -179,6 +189,7 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): mbox[self.CREATED_KEY] = creation_ts doc = self._soledad.create_doc(mbox) + self._load_mailboxes() return bool(doc) def create(self, pathspec): @@ -209,6 +220,7 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): except imap4.MailboxCollision: if not pathspec.endswith('/'): return False + self._load_mailboxes() return True def select(self, name, readwrite=1): @@ -221,13 +233,13 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): :param readwrite: 1 for readwrite permissions. :type readwrite: int - :rtype: bool + :rtype: SoledadMailbox """ name = self._parse_mailbox_name(name) if name not in self.mailboxes: + logger.warning("No such mailbox!") return None - self.selected = name return SoledadMailbox( @@ -266,6 +278,7 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): "Hierarchically inferior mailboxes " "exist and \\Noselect is set") mbox.destroy() + self._load_mailboxes() # XXX FIXME --- not honoring the inferior names... @@ -303,6 +316,8 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): mbox.content[self.MBOX_KEY] = new self._soledad.put_doc(mbox) + self._load_mailboxes() + # XXX ---- FIXME!!!! ------------------------------------ # until here we just renamed the index... # We have to rename also the occurrence of this -- cgit v1.2.3 From ed270dcbdca5f3887953121aa0750f99b618dd3a Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Mon, 17 Feb 2014 10:45:39 -0400 Subject: profile select --- src/leap/mail/imap/account.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'src/leap/mail/imap/account.py') diff --git a/src/leap/mail/imap/account.py b/src/leap/mail/imap/account.py index fd35698..1b5d4a0 100644 --- a/src/leap/mail/imap/account.py +++ b/src/leap/mail/imap/account.py @@ -19,9 +19,11 @@ Soledad Backed Account. """ import copy import logging +import os import time from twisted.mail import imap4 +from twisted.python import log from zope.interface import implements from leap.common.check import leap_assert, leap_assert_type @@ -33,6 +35,15 @@ from leap.soledad.client import Soledad logger = logging.getLogger(__name__) +PROFILE_CMD = os.environ.get('LEAP_PROFILE_IMAPCMD', False) + +if PROFILE_CMD: + + def _debugProfiling(result, cmdname, start): + took = (time.time() - start) * 1000 + log.msg("CMD " + cmdname + " TOOK: " + str(took) + " msec") + return result + ####################################### # Soledad Account @@ -235,15 +246,20 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): :rtype: SoledadMailbox """ - name = self._parse_mailbox_name(name) + if PROFILE_CMD: + start = time.time() + name = self._parse_mailbox_name(name) if name not in self.mailboxes: logger.warning("No such mailbox!") return None self.selected = name - return SoledadMailbox( + sm = SoledadMailbox( name, self._soledad, self._memstore, readwrite) + if PROFILE_CMD: + _debugProfiling(None, "SELECT", start) + return sm def delete(self, name, force=False): """ -- cgit v1.2.3 From d6ee618534596e76733d3c6b6cf0a75bdb4aa905 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 18 Feb 2014 09:58:54 -0400 Subject: catch soledad error while updating mbox doc --- src/leap/mail/imap/account.py | 1 + 1 file changed, 1 insertion(+) (limited to 'src/leap/mail/imap/account.py') diff --git a/src/leap/mail/imap/account.py b/src/leap/mail/imap/account.py index 1b5d4a0..ede63d3 100644 --- a/src/leap/mail/imap/account.py +++ b/src/leap/mail/imap/account.py @@ -119,6 +119,7 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): :rtype: SoledadDocument """ + # XXX use soledadstore instead ...; doc = self._soledad.get_from_index( self.TYPE_MBOX_IDX, self.MBOX_KEY, self._parse_mailbox_name(name)) -- cgit v1.2.3 From 733994d68b9f3ce528b552f67e9cbec005e57e9f Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 25 Feb 2014 22:38:29 -0400 Subject: rename all fdocs when folder is renamed --- src/leap/mail/imap/account.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'src/leap/mail/imap/account.py') diff --git a/src/leap/mail/imap/account.py b/src/leap/mail/imap/account.py index ede63d3..199a2a4 100644 --- a/src/leap/mail/imap/account.py +++ b/src/leap/mail/imap/account.py @@ -329,20 +329,13 @@ class SoledadBackedAccount(WithMsgFields, IndexedDB, MBoxParser): raise imap4.MailboxCollision(repr(new)) for (old, new) in inferiors: + self._memstore.rename_fdocs_mailbox(old, new) mbox = self._get_mailbox_by_name(old) mbox.content[self.MBOX_KEY] = new self._soledad.put_doc(mbox) self._load_mailboxes() - # XXX ---- FIXME!!!! ------------------------------------ - # until here we just renamed the index... - # We have to rename also the occurrence of this - # mailbox on ALL the messages that are contained in it!!! - # ... we maybe could use a reference to the doc_id - # in each msg, instead of the "mbox" field in msgs - # ------------------------------------------------------- - def _inferiorNames(self, name): """ Return hierarchically inferior mailboxes. -- cgit v1.2.3