diff options
-rw-r--r-- | mail/changes/feature_3480_add_imap_events | 1 | ||||
-rw-r--r-- | mail/src/leap/mail/imap/fetch.py | 60 | ||||
-rw-r--r-- | mail/src/leap/mail/imap/service/imap.py | 34 |
3 files changed, 76 insertions, 19 deletions
diff --git a/mail/changes/feature_3480_add_imap_events b/mail/changes/feature_3480_add_imap_events new file mode 100644 index 00000000..fc503e82 --- /dev/null +++ b/mail/changes/feature_3480_add_imap_events @@ -0,0 +1 @@ + o Add events for notifications about imap activity. Closes: #3480 diff --git a/mail/src/leap/mail/imap/fetch.py b/mail/src/leap/mail/imap/fetch.py index 9b76592f..267af381 100644 --- a/mail/src/leap/mail/imap/fetch.py +++ b/mail/src/leap/mail/imap/fetch.py @@ -1,15 +1,43 @@ +# -*- coding: utf-8 -*- +# fetch.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 <http://www.gnu.org/licenses/>. +""" +Incoming mail fetcher. +""" import logging import json import ssl +import time from twisted.python import log from twisted.internet import defer from twisted.internet.task import LoopingCall from twisted.internet.threads import deferToThread +from leap.common import events as leap_events from leap.common.check import leap_assert, leap_assert_type from leap.soledad import Soledad +from leap.common.events.events_pb2 import IMAP_FETCHED_INCOMING +from leap.common.events.events_pb2 import IMAP_MSG_PROCESSING +from leap.common.events.events_pb2 import IMAP_MSG_DECRYPTED +from leap.common.events.events_pb2 import IMAP_MSG_SAVED_LOCALLY +from leap.common.events.events_pb2 import IMAP_MSG_DELETED_INCOMING + + logger = logging.getLogger(__name__) @@ -100,8 +128,12 @@ class LeapIncomingMail(object): try: self._soledad.sync() + fetched_ts = time.mktime(time.gmtime()) doclist = self._soledad.get_from_index("just-mail", "*") - log.msg("there are %s mails" % (len(doclist),)) + num_mails = len(doclist) + log.msg("there are %s mails" % (num_mails,)) + leap_events.signal( + IMAP_FETCHED_INCOMING, str(num_mails), str(fetched_ts)) return doclist except ssl.SSLError as exc: logger.warning('SSL Error while syncing soledad: %r' % (exc,)) @@ -117,8 +149,12 @@ class LeapIncomingMail(object): if not doclist: logger.debug("no docs found") return - for doc in doclist: - logger.debug("processing doc: %s" % doc) + num_mails = len(doclist) + for index, doc in enumerate(doclist): + logger.debug("processing doc %d of %d: %s" % ( + index, num_mails, doc)) + leap_events.signal( + IMAP_MSG_PROCESSING, str(index), str(num_mails)) keys = doc.content.keys() if self.ENC_SCHEME_KEY in keys and self.ENC_JSON_KEY in keys: @@ -133,11 +169,17 @@ class LeapIncomingMail(object): def _decrypt_msg(self, doc, encdata): log.msg('decrypting msg') key = self._pkey - decrdata = (self._keymanager.decrypt( - encdata, key, - # XXX get from public method instead - passphrase=self._soledad._passphrase)) - + try: + decrdata = (self._keymanager.decrypt( + encdata, key, + # XXX get from public method instead + passphrase=self._soledad._passphrase)) + ok = True + except Exception as exc: + logger.warning("Error while decrypting msg: %r" % (exc,)) + decrdata = "" + ok = False + leap_events.signal(IMAP_MSG_DECRYPTED, ok) # XXX TODO: defer this properly return self._process_decrypted(doc, decrdata) @@ -183,8 +225,10 @@ class LeapIncomingMail(object): rawmsg = rawmsg.replace(pgp_message, decrdata) # add to inbox and delete from soledad self._inbox.addMessage(rawmsg, (self.RECENT_FLAG,)) + leap_events.signal(IMAP_MSG_SAVED_LOCALLY) doc_id = doc.doc_id self._soledad.delete_doc(doc) log.msg("deleted doc %s from incoming" % doc_id) + leap_events.signal(IMAP_MSG_DELETED_INCOMING) except Exception as e: logger.error("Problem processing incoming mail: %r" % (e,)) diff --git a/mail/src/leap/mail/imap/service/imap.py b/mail/src/leap/mail/imap/service/imap.py index 1a8c15cf..380324c7 100644 --- a/mail/src/leap/mail/imap/service/imap.py +++ b/mail/src/leap/mail/imap/service/imap.py @@ -27,6 +27,7 @@ from twisted.internet.protocol import ServerFactory from twisted.mail import imap4 from twisted.python import log +from leap.common import events as leap_events from leap.common.check import leap_assert, leap_assert_type from leap.keymanager import KeyManager from leap.mail.imap.server import SoledadBackedAccount @@ -42,6 +43,10 @@ INCOMING_CHECK_PERIOD = 5 # The period between succesive checks of the incoming mail # queue (in seconds) +from leap.common.events.events_pb2 import IMAP_SERVICE_STARTED +from leap.common.events.events_pb2 import IMAP_SERVICE_FAILED_TO_START +from leap.common.events.events_pb2 import IMAP_CLIENT_LOGIN + class LeapIMAPServer(imap4.IMAP4Server): """ @@ -84,6 +89,7 @@ class LeapIMAPServer(imap4.IMAP4Server): def authenticateLogin(self, username, password): # all is allowed so far. use realm instead + leap_events.signal(IMAP_CLIENT_LOGIN, True) return imap4.IAccount, self.theAccount, lambda: None @@ -150,15 +156,21 @@ def run_service(*args, **kwargs): factory = LeapIMAPFactory(uuid, soledad) from twisted.internet import reactor - reactor.listenTCP(port, factory) - - fetcher = LeapIncomingMail( - keymanager, - soledad, - factory.theAccount, - check_period) - - fetcher.start_loop() - logger.debug("IMAP4 Server is RUNNING in port %s" % (port,)) - return fetcher + try: + reactor.listenTCP(port, factory) + fetcher = LeapIncomingMail( + keymanager, + soledad, + factory.theAccount, + check_period) + except Exception as exc: + # XXX cannot listen? + logger.error("Error launching IMAP service: %r" % (exc,)) + leap_events.signal(IMAP_SERVICE_FAILED_TO_START, str(port)) + return + else: + fetcher.start_loop() + logger.debug("IMAP4 Server is RUNNING in port %s" % (port,)) + leap_events.signal(IMAP_SERVICE_STARTED, str(port)) + return fetcher |