diff options
Diffstat (limited to 'src/leap/mail/imap')
-rw-r--r-- | src/leap/mail/imap/mailbox.py | 5 | ||||
-rw-r--r-- | src/leap/mail/imap/server.py | 4 | ||||
-rw-r--r-- | src/leap/mail/imap/service/imap.py | 14 | ||||
-rwxr-xr-x | src/leap/mail/imap/tests/getmail | 102 |
4 files changed, 99 insertions, 26 deletions
diff --git a/src/leap/mail/imap/mailbox.py b/src/leap/mail/imap/mailbox.py index c52a2e3..c7accbb 100644 --- a/src/leap/mail/imap/mailbox.py +++ b/src/leap/mail/imap/mailbox.py @@ -215,11 +215,13 @@ class IMAPMailbox(object): but in the future will be useful to get absolute UIDs from message sequence numbers. + :param message: the message sequence number. :type message: int :rtype: int :return: the UID of the message. + """ # TODO support relative sequences. The (imap) message should # receive a sequence number attribute: a deferred is not expected @@ -558,7 +560,8 @@ class IMAPMailbox(object): def _get_imap_msg(messages): d_imapmsg = [] - for msg in messages: + # just in case we got bad data in here + for msg in filter(None, messages): d_imapmsg.append(getimapmsg(msg)) return defer.gatherResults(d_imapmsg, consumeErrors=True) diff --git a/src/leap/mail/imap/server.py b/src/leap/mail/imap/server.py index 39f483f..8f14936 100644 --- a/src/leap/mail/imap/server.py +++ b/src/leap/mail/imap/server.py @@ -27,7 +27,7 @@ from twisted.mail import imap4 from twisted.python import log from leap.common.check import leap_assert, leap_assert_type -from leap.common.events import emit, catalog +from leap.common.events import emit_async, catalog from leap.soledad.client import Soledad # imports for LITERAL+ patch @@ -224,7 +224,7 @@ class LEAPIMAPServer(imap4.IMAP4Server): # bad username, reject. raise cred.error.UnauthorizedLogin() # any dummy password is allowed so far. use realm instead! - emit(catalog.IMAP_CLIENT_LOGIN, "1") + emit_async(catalog.IMAP_CLIENT_LOGIN, "1") return imap4.IAccount, self.theAccount, lambda: None def do_FETCH(self, tag, messages, query, uid=0): diff --git a/src/leap/mail/imap/service/imap.py b/src/leap/mail/imap/service/imap.py index c3ae59a..a50611b 100644 --- a/src/leap/mail/imap/service/imap.py +++ b/src/leap/mail/imap/service/imap.py @@ -28,7 +28,7 @@ from twisted.internet.protocol import ServerFactory from twisted.mail import imap4 from twisted.python import log -from leap.common.events import emit, catalog +from leap.common.events import emit_async, catalog from leap.common.check import leap_check from leap.mail.imap.account import IMAPAccount from leap.mail.imap.server import LEAPIMAPServer @@ -158,8 +158,14 @@ def run_service(store, **kwargs): factory = LeapIMAPFactory(uuid, userid, store) try: + interface = "localhost" + # don't bind just to localhost if we are running on docker since we + # won't be able to access imap from the host + if os.environ.get("LEAP_DOCKERIZED"): + interface = '' + tport = reactor.listenTCP(port, factory, - interface="localhost") + interface=interface) except CannotListenError: logger.error("IMAP Service failed to start: " "cannot listen in port %s" % (port,)) @@ -178,10 +184,10 @@ def run_service(store, **kwargs): reactor.listenTCP(manhole.MANHOLE_PORT, manhole_factory, interface="127.0.0.1") logger.debug("IMAP4 Server is RUNNING in port %s" % (port,)) - emit(catalog.IMAP_SERVICE_STARTED, str(port)) + emit_async(catalog.IMAP_SERVICE_STARTED, str(port)) # FIXME -- change service signature return tport, factory # not ok, signal error. - emit(catalog.IMAP_SERVICE_FAILED_TO_START, str(port)) + emit_async(catalog.IMAP_SERVICE_FAILED_TO_START, str(port)) diff --git a/src/leap/mail/imap/tests/getmail b/src/leap/mail/imap/tests/getmail index 0fb00d2..dd3fa0b 100755 --- a/src/leap/mail/imap/tests/getmail +++ b/src/leap/mail/imap/tests/getmail @@ -10,6 +10,7 @@ Simple IMAP4 client which displays the subjects of all messages in a particular mailbox. """ +import os import sys from twisted.internet import protocol @@ -20,6 +21,9 @@ from twisted.mail import imap4 from twisted.protocols import basic from twisted.python import log +# Global options stored here from main +_opts = {} + class TrivialPrompter(basic.LineReceiver): from os import linesep as delimiter @@ -70,9 +74,7 @@ class SimpleIMAP4ClientFactory(protocol.ClientFactory): """ Initiate the protocol instance. Since we are building a simple IMAP client, we don't bother checking what capabilities the server has. We - just add all the authenticators twisted.mail has. Note: Gmail no - longer uses any of the methods below, it's been using XOAUTH since - 2010. + just add all the authenticators twisted.mail has. """ assert not self.usedUp self.usedUp = True @@ -159,14 +161,24 @@ def InsecureLogin(proto, username, password): def cbMailboxList(result, proto): """ Callback invoked when a list of mailboxes has been retrieved. + If we have a selected mailbox in the global options, we directly pick it. + Otherwise, we offer a prompt to let user choose one. """ - result = [e[2] for e in result] - s = '\n'.join(['%d. %s' % (n + 1, m) for (n, m) in zip(range(len(result)), result)]) + all_mbox_list = [e[2] for e in result] + s = '\n'.join(['%d. %s' % (n + 1, m) for (n, m) in zip(range(len(all_mbox_list)), all_mbox_list)]) if not s: return defer.fail(Exception("No mailboxes exist on server!")) - return proto.prompt(s + "\nWhich mailbox? [1] " - ).addCallback(cbPickMailbox, proto, result - ) + + selected_mailbox = _opts.get('mailbox') + + if not selected_mailbox: + return proto.prompt(s + "\nWhich mailbox? [1] " + ).addCallback(cbPickMailbox, proto, all_mbox_list + ) + else: + mboxes_lower = map(lambda s: s.lower(), all_mbox_list) + index = mboxes_lower.index(selected_mailbox.lower()) + 1 + return cbPickMailbox(index, proto, all_mbox_list) def cbPickMailbox(result, proto, mboxes): @@ -194,18 +206,34 @@ def cbExamineMbox(result, proto): def cbFetch(result, proto): """ - Display headers. + Display a listing of the messages in the mailbox, based on the collected + headers. """ + selected_subject = _opts.get('subject', None) + index = None + if result: keys = result.keys() keys.sort() - for k in keys: - proto.display('%s %s' % (k, result[k][0][2])) + + if selected_subject: + for k in keys: + # remove 'Subject: ' preffix plus eol + subject = result[k][0][2][9:].rstrip('\r\n') + if subject.lower() == selected_subject.lower(): + index = k + break + else: + for k in keys: + proto.display('%s %s' % (k, result[k][0][2])) else: print "Hey, an empty mailbox!" - return proto.prompt("\nWhich message? [1] (Q quits) " - ).addCallback(cbPickMessage, proto) + if not index: + return proto.prompt("\nWhich message? [1] (Q quits) " + ).addCallback(cbPickMessage, proto) + else: + return cbPickMessage(index, proto) def cbPickMessage(result, proto): @@ -247,16 +275,53 @@ def cbClose(result): def main(): + import argparse + import ConfigParser import sys + from twisted.internet import reactor + + description = ( + 'Get messages from a LEAP IMAP Proxy.\nThis is a ' + 'debugging tool, do not use this to retrieve any sensitive ' + 'information, or we will send ninjas to your house!') + epilog = ( + 'In case you want to automate the usage of this utility ' + 'you can place your credentials in a file pointed by ' + 'BITMASK_CREDENTIALS. You need to have a [Credentials] ' + 'section, with username=<user@provider> and password fields') + + parser = argparse.ArgumentParser(description=description, epilog=epilog) + credentials = os.environ.get('BITMASK_CREDENTIALS') + + if credentials: + try: + config = ConfigParser.ConfigParser() + config.read(credentials) + username = config.get('Credentials', 'username') + password = config.get('Credentials', 'password') + except Exception, e: + print "Error reading credentials file: {0}".format(e) + sys.exit() + else: + parser.add_argument('username', type=str) + parser.add_argument('password', type=str) - if len(sys.argv) != 3: - print "Usage: getmail <user> <pass>" - sys.exit() + parser.add_argument('--mailbox', dest='mailbox', default=None, + help='Which mailbox to retrieve. Empty for interactive prompt.') + parser.add_argument('--subject', dest='subject', default=None, + help='A subject for retrieve a mail that matches. Empty for interactive prompt.') + + ns = parser.parse_args() + + if not credentials: + username = ns.username + password = ns.password + + _opts['mailbox'] = ns.mailbox + _opts['subject'] = ns.subject hostname = "localhost" port = "1984" - username = sys.argv[1] - password = sys.argv[2] onConn = defer.Deferred( ).addCallback(cbServerGreeting, username, password @@ -265,7 +330,6 @@ def main(): factory = SimpleIMAP4ClientFactory(username, onConn) - from twisted.internet import reactor if port == '993': reactor.connectSSL( hostname, int(port), factory, ssl.ClientContextFactory()) |