From 82a2b6d45751d671c593c15144c908b665e955bb Mon Sep 17 00:00:00 2001 From: Ruben Pollan Date: Mon, 22 Dec 2014 09:04:29 -0600 Subject: Use gpg key directly _build_key_from_gpg in keymanager has changed --- src/leap/mx/mail_receiver.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'src/leap/mx/mail_receiver.py') diff --git a/src/leap/mx/mail_receiver.py b/src/leap/mx/mail_receiver.py index dd76f08..d3f3c68 100644 --- a/src/leap/mx/mail_receiver.py +++ b/src/leap/mx/mail_receiver.py @@ -224,10 +224,6 @@ class MailReceiver(Service): with openpgp.TempGPGWrapper(gpgbinary='/usr/bin/gpg') as gpg: gpg.import_keys(pubkey) key = gpg.list_keys().pop() - # We don't care about the actual address, so we use a - # dummy one, we just care about the import of the pubkey - openpgp_key = openpgp._build_key_from_gpg("dummy@mail.com", - key, pubkey) # add X-Leap-Provenance header if message is not encrypted if message.get_content_type() != 'multipart/encrypted' and \ @@ -236,7 +232,7 @@ class MailReceiver(Service): message.add_header( 'X-Leap-Provenance', email.utils.formatdate(), - pubkey=openpgp_key.key_id) + pubkey=key["keyid"]) data = {'incoming': True, 'content': message.as_string()} doc.content = { self.INCOMING_KEY: True, @@ -244,7 +240,7 @@ class MailReceiver(Service): ENC_SCHEME_KEY: EncryptionSchemes.PUBKEY, ENC_JSON_KEY: str(gpg.encrypt( json.dumps(data, ensure_ascii=False), - openpgp_key.fingerprint, + key["fingerprint"], symmetric=False)) } -- cgit v1.2.3 From e660453542889fc8285217f008803c9701f1b778 Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 5 Feb 2015 10:04:12 -0200 Subject: Process unprocessed mail when MX starts (closes #2591). --- src/leap/mx/mail_receiver.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'src/leap/mx/mail_receiver.py') diff --git a/src/leap/mx/mail_receiver.py b/src/leap/mx/mail_receiver.py index d3f3c68..2d91fda 100644 --- a/src/leap/mx/mail_receiver.py +++ b/src/leap/mx/mail_receiver.py @@ -134,6 +134,7 @@ class MailReceiver(Service): INCOMING_KEY = 'incoming' ERROR_DECRYPTING_KEY = "errdecr" + PROCESS_SKIPPED_INTERVAL = 60 * 30 # every half an hour def __init__(self, mail_couch_url, users_cdb, directories, bounce_from, bounce_subject): @@ -171,17 +172,18 @@ class MailReceiver(Service): self.wm = inotify.INotify() self.wm.startReading() + # watch the mail directory for new files and process incoming mail mask = inotify.IN_CREATE - for directory, recursive in self._directories: log.msg("Watching %r --- Recursive: %r" % (directory, recursive)) self.wm.watch(filepath.FilePath(directory), mask, callbacks=[self._process_incoming_email], recursive=recursive) + # schedule a periodic task to process skipped mail, but also run it + # immediatelly self._lcall = task.LoopingCall(self._process_skipped) - # Run once every half an hour, but don't start right now - self._lcall.start(interval=60*30, now=False) + self._lcall.start(interval=self.PROCESS_SKIPPED_INTERVAL, now=True) def _encrypt_message(self, pubkey, message): """ @@ -380,7 +382,7 @@ class MailReceiver(Service): self._processing_skipped = True try: log.msg("Starting processing skipped mail...") - log.msg("-"*50) + log.msg("-" * 50) for directory, recursive in self._directories: for root, dirs, files in os.walk(directory): @@ -390,7 +392,7 @@ class MailReceiver(Service): fpath = filepath.FilePath(fullpath) yield self._step_process_mail_backend(fpath) except Exception: - log.msg("Error processing skipped mail: %r" % \ + log.msg("Error processing skipped mail: %r" % (fullpath,)) log.err() if not recursive: @@ -401,7 +403,7 @@ class MailReceiver(Service): finally: self._processing_skipped = False - log.msg("+"*50) + log.msg("+" * 50) log.msg("Done processing skipped mail") @defer.inlineCallbacks @@ -463,9 +465,10 @@ class MailReceiver(Service): """ try: while self._processing_skipped: - log.msg("Waiting for the process of skipped mail to be done...") + log.msg("Waiting for the process of skipped mail to be " + "done...") yield self.sleep(10) # NO-OP - if os.path.split(filepath.dirname())[-1] == "new": + if os.path.split(filepath.dirname())[-1] == "new": yield self._step_process_mail_backend(filepath) except Exception as e: log.msg("Something went wrong while processing {0!r}: {1!r}" -- cgit v1.2.3 From 91404f56d200ba01130ff208169d45f615f8a094 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 5 Feb 2015 10:46:57 -0400 Subject: Add ability to trigger processing on SIGUSR1 (Related: #2591) --- src/leap/mx/mail_receiver.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'src/leap/mx/mail_receiver.py') diff --git a/src/leap/mx/mail_receiver.py b/src/leap/mx/mail_receiver.py index 2d91fda..5fa3863 100644 --- a/src/leap/mx/mail_receiver.py +++ b/src/leap/mx/mail_receiver.py @@ -15,9 +15,13 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . - """ -MailReceiver service definition +MailReceiver service definition. This service monitors the incoming mail and +process it. + +The MailReceiver service is configured to process incoming any skipped mail +every half an hour, and also on service start. It can be forced to start +processing by sending SIGUSR1 to the process. If there's a user facing problem when processing an email, it will be bounced back to the sender. @@ -29,9 +33,9 @@ User facing problems could be: Any other problem is a bug, which will be logged. Until the bug is fixed, the email will stay in there waiting. """ - import os import uuid as pyuuid +import signal import json import email.utils @@ -43,10 +47,12 @@ from email.MIMEText import MIMEText from email.Utils import formatdate from email.header import decode_header -from twisted.application.service import Service +from twisted.application.service import Service, IService from twisted.internet import inotify, defer, task, reactor from twisted.python import filepath, log +from zope.interface import implements + from leap.soledad.common.crypto import ( EncryptionSchemes, ENC_JSON_KEY, @@ -129,8 +135,9 @@ def async_check_output(args, msg): class MailReceiver(Service): """ - Service that monitors incoming email and processes it + Service that monitors incoming email and processes it. """ + implements(IService) INCOMING_KEY = 'incoming' ERROR_DECRYPTING_KEY = "errdecr" @@ -144,13 +151,17 @@ class MailReceiver(Service): :param mail_couch_url: URL prefix for the couchdb where mail should be stored :type mail_couch_url: str + :param users_cdb: CouchDB instance from where to get the uuid - and pubkey for a user + and pubkey for a user :type users_cdb: ConnectedCouchDB + :param directories: list of directories to monitor :type directories: list of tuples (path: str, recursive: bool) + :param bounce_from: Email address of the bouncer :type bounce_from: str + :param bounce_subject: Subject line used in the bounced mail :type bounce_subject: str """ @@ -164,6 +175,11 @@ class MailReceiver(Service): self._bounce_from = bounce_from self._bounce_subject = bounce_subject + def _signal_handler(sig_num, stack_frame): + self._process_skipped() + + signal.signal(signal.SIGUSR1, _signal_handler) + def startService(self): """ Starts the MailReceiver service -- cgit v1.2.3 From de95cc2fc21756c1b021723f49e846298e3b38a2 Mon Sep 17 00:00:00 2001 From: drebs Date: Fri, 6 Feb 2015 16:48:13 -0200 Subject: Retry watching dirs if failed for some reason (closes #6687). --- src/leap/mx/mail_receiver.py | 68 +++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 16 deletions(-) (limited to 'src/leap/mx/mail_receiver.py') diff --git a/src/leap/mx/mail_receiver.py b/src/leap/mx/mail_receiver.py index 5fa3863..630c982 100644 --- a/src/leap/mx/mail_receiver.py +++ b/src/leap/mx/mail_receiver.py @@ -143,6 +143,12 @@ class MailReceiver(Service): ERROR_DECRYPTING_KEY = "errdecr" PROCESS_SKIPPED_INTERVAL = 60 * 30 # every half an hour + """ + If there's a failure when trying to watch a directory for file creation, + the service will schedule a retry delayed by the following amount of time. + """ + RETRY_DIR_WATCH_DELAY = 60 * 5 # 5 minutes + def __init__(self, mail_couch_url, users_cdb, directories, bounce_from, bounce_subject): """ @@ -165,20 +171,15 @@ class MailReceiver(Service): :param bounce_subject: Subject line used in the bounced mail :type bounce_subject: str """ - # Service doesn't define an __init__ + # IService doesn't define an __init__ self._mail_couch_url = mail_couch_url self._users_cdb = users_cdb self._directories = directories - self._domain = socket.gethostbyaddr(socket.gethostname())[0] - self._processing_skipped = False - self._bounce_from = bounce_from self._bounce_subject = bounce_subject - def _signal_handler(sig_num, stack_frame): - self._process_skipped() - - signal.signal(signal.SIGUSR1, _signal_handler) + self._domain = socket.gethostbyaddr(socket.gethostname())[0] + self._processing_skipped = False def startService(self): """ @@ -188,19 +189,55 @@ class MailReceiver(Service): self.wm = inotify.INotify() self.wm.startReading() - # watch the mail directory for new files and process incoming mail - mask = inotify.IN_CREATE + # watch mail directories for new files to trigger processing of + # incoming mail for directory, recursive in self._directories: - log.msg("Watching %r --- Recursive: %r" % (directory, recursive)) - self.wm.watch(filepath.FilePath(directory), mask, - callbacks=[self._process_incoming_email], - recursive=recursive) + self._start_watching_dir(directory, recursive) - # schedule a periodic task to process skipped mail, but also run it + # schedule a periodic task to process skipped mail, and also run it # immediatelly self._lcall = task.LoopingCall(self._process_skipped) self._lcall.start(interval=self.PROCESS_SKIPPED_INTERVAL, now=True) + # catch SIGUSR1 to trigger processing of skipped mail + signal.signal( + signal.SIGUSR1, + lambda *_: self._process_skipped()) + + def stopService(self): + """ + Stop the MailReceiver service + """ + self.wm.stopReading() + self._lcall.stop() + + def _start_watching_dir(self, dirname, recursive): + """ + Start watching a directory to trigger processing of newly created + files. + + Will also add a delayed call to retry when failed for some reason. + """ + directory = filepath.FilePath(dirname) + try: + if not directory.isdir(): + raise OSError("Not a directory: '%s'" % directory.path) + self.wm.watch( + directory, + inotify.IN_CREATE, + callbacks=[self._process_incoming_email], + recursive=recursive) + log.msg("Watching %r --- Recursive: %r" % (directory, recursive)) + except Exception as e: + log.msg( + "Failed adding watch to %s, will try again in %s seconds: %s" + % (directory, self.RETRY_DIR_WATCH_DELAY, e)) + reactor.callLater( + self.RETRY_DIR_WATCH_DELAY, + self._start_watching_dir, + dirname, + recursive) + def _encrypt_message(self, pubkey, message): """ Given a public key and a message, it encrypts the message to @@ -380,7 +417,6 @@ class MailReceiver(Service): :rtype: twisted.internet.defer.Deferred """ - from twisted.internet import reactor d = defer.Deferred() reactor.callLater(secs, d.callback, None) return d -- cgit v1.2.3 From 938cada00298243f0cf51c9bfd460ecb16938b57 Mon Sep 17 00:00:00 2001 From: drebs Date: Tue, 24 Mar 2015 15:07:51 -0300 Subject: [bug] correctly return async bouncer deferred --- src/leap/mx/mail_receiver.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/leap/mx/mail_receiver.py') diff --git a/src/leap/mx/mail_receiver.py b/src/leap/mx/mail_receiver.py index 630c982..7856594 100644 --- a/src/leap/mx/mail_receiver.py +++ b/src/leap/mx/mail_receiver.py @@ -96,6 +96,10 @@ class BouncerSubprocessProtocol(protocol.ProcessProtocol): self._errBuffer = "" self._d = None + @property + def deferred(self): + return self._d + def connectionMade(self): self._d = defer.Deferred() @@ -130,7 +134,7 @@ def async_check_output(args, msg): """ pprotocol = BouncerSubprocessProtocol(msg) reactor.spawnProcess(pprotocol, args[0], args) - return pprotocol.d + return pprotocol.deferred class MailReceiver(Service): -- cgit v1.2.3 From 14ef3dcce18240b756415fefa2a56936f96a12e9 Mon Sep 17 00:00:00 2001 From: drebs Date: Mon, 13 Apr 2015 15:58:16 -0300 Subject: [bug] fix bounce message recipient The bounce message was using the original message's "From:" header instead of the "To:" header to indicate the original recipient. This commit fixes that. Closes: #6854. Releases: 0.6.2, 0.7.0 --- src/leap/mx/mail_receiver.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/leap/mx/mail_receiver.py') diff --git a/src/leap/mx/mail_receiver.py b/src/leap/mx/mail_receiver.py index 7856594..77909b0 100644 --- a/src/leap/mx/mail_receiver.py +++ b/src/leap/mx/mail_receiver.py @@ -392,15 +392,16 @@ class MailReceiver(Service): :param reason: Brief explanation about why it's being bounced :type reason: str """ - to = orig_msg.get("From") + orig_from = orig_msg.get("From") + orig_to = orig_msg.get("To") msg = MIMEMultipart() msg['From'] = self._bounce_from - msg['To'] = to + msg['To'] = orig_from msg['Date'] = formatdate(localtime=True) msg['Subject'] = self._bounce_subject - decoded_to = " ".join([x[0] for x in decode_header(to)]) + decoded_to = " ".join([x[0] for x in decode_header(orig_to)]) text = BOUNCE_TEMPLATE.format(decoded_to, reason, orig_msg.as_string()) -- cgit v1.2.3 From 527d7d4a67f859a3315812b100b2c58fd0eeded6 Mon Sep 17 00:00:00 2001 From: drebs Date: Wed, 15 Apr 2015 12:48:34 -0300 Subject: [bug] return uuid as result of alias resolver This fixes a bug introduced on b0ef529cc882a96903597fb5279919969fa286c3, when the alias resolver was modified to return the user's address instead of the uuid. In order to fix this, I had to revert one of the changes made by the commit above, which is to don't make use of reduced view for the uuid query. The pgp public key query remains reduced, as implemented in the commit above. We also refactor the code a bit to allow for log messages specific to each of tcp map's sublasses. Related: #6858. --- src/leap/mx/mail_receiver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/mx/mail_receiver.py') diff --git a/src/leap/mx/mail_receiver.py b/src/leap/mx/mail_receiver.py index 77909b0..f0b9c03 100644 --- a/src/leap/mx/mail_receiver.py +++ b/src/leap/mx/mail_receiver.py @@ -491,7 +491,7 @@ class MailReceiver(Service): log.msg("BUG: There was no uuid!") defer.returnValue(None) - pubkey = yield self._users_cdb.getPubKey(uuid) + pubkey = yield self._users_cdb.getPubkey(uuid) if pubkey is None or len(pubkey) == 0: log.msg("No public key, stopping the processing chain") bounce_reason = "Missing PubKey: There was a problem " \ -- cgit v1.2.3 From ae90151c632b376abc2a5bdf76d136b3a3629ea6 Mon Sep 17 00:00:00 2001 From: drebs Date: Wed, 15 Apr 2015 14:16:13 -0300 Subject: [bug] fix extraction of uuid from message headers Before this commit, the mail receiver system used to compare the domain of the delivery addresses found in the "Delivered-To" header to find out the final delivery address. If we assume that the mail server delivery to the spool mail directory was correct, then we have two facts: (1) the topmost "Delivered-To" header is the one that indicates the correct final delivery address; and (2) we should expect the address to be @ because of the earlier alias resolve query made by the mail server. Another problem is that the domain comparison would compare whatever is in the "Delivered-To" header with whatever the python's socket module would return, which depends on the values on /etc/hosts and the order of the values in that file. This was causing problems whenever the platform made changes in /etc/hosts. So this commit eliminates the domain check and gets the uuid from the first "Delivered-To" header found in the message. Related: #6858. --- src/leap/mx/mail_receiver.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'src/leap/mx/mail_receiver.py') diff --git a/src/leap/mx/mail_receiver.py b/src/leap/mx/mail_receiver.py index f0b9c03..6b384f2 100644 --- a/src/leap/mx/mail_receiver.py +++ b/src/leap/mx/mail_receiver.py @@ -39,7 +39,6 @@ import signal import json import email.utils -import socket from email import message_from_string from email.MIMEMultipart import MIMEMultipart @@ -181,8 +180,6 @@ class MailReceiver(Service): self._directories = directories self._bounce_from = bounce_from self._bounce_subject = bounce_subject - - self._domain = socket.gethostbyaddr(socket.gethostname())[0] self._processing_skipped = False def startService(self): @@ -357,7 +354,7 @@ class MailReceiver(Service): def _get_owner(self, mail): """ - Given an email, returns the uuid of the owner. + Given an email, return the uuid of the owner. :param mail: mail to analyze :type mail: email.message.Message @@ -365,18 +362,17 @@ class MailReceiver(Service): :returns: uuid :rtype: str or None """ - uuid = None - + # we expect the topmost "Delivered-To" header to indicate the correct + # final delivery address. It should consist of @, as the + # earlier alias resolver query should have translated the username to + # the user id. See https://leap.se/code/issues/6858 for more info. delivereds = mail.get_all("Delivered-To") if delivereds is None: + # XXX this should not happen! see the comment above return None - for to in delivereds: - name, addr = email.utils.parseaddr(to) - parts = addr.split("@") - if len(parts) > 1 and parts[1] == self._domain: - uuid = parts[0] - break - + final_address = delivereds.pop(0) + _, addr = email.utils.parseaddr(final_address) + uuid, _ = addr.split("@") return uuid @defer.inlineCallbacks -- cgit v1.2.3 From 3353e2bccb2625ae06472721cfbb8cf53144a255 Mon Sep 17 00:00:00 2001 From: drebs Date: Wed, 15 Apr 2015 14:49:56 -0300 Subject: [bug] implement message bouncing according to RFCs If we do not adhere to the standads, we may have a lot of problems when bouncing a message. This commit implements a bounce message according to: * RFC 6522 - The Multipart/Report Media Type for the Reporting of Mail System Administrative Messages * RFC 3834 - Do not bounce for unknown or invalid addresses. * RFC 3464 - An Extensible Message Format for Delivery Status Notification. Closes: #6858. --- src/leap/mx/mail_receiver.py | 132 ++++++++----------------------------------- 1 file changed, 23 insertions(+), 109 deletions(-) (limited to 'src/leap/mx/mail_receiver.py') diff --git a/src/leap/mx/mail_receiver.py b/src/leap/mx/mail_receiver.py index 6b384f2..446fd38 100644 --- a/src/leap/mx/mail_receiver.py +++ b/src/leap/mx/mail_receiver.py @@ -41,10 +41,6 @@ import json import email.utils from email import message_from_string -from email.MIMEMultipart import MIMEMultipart -from email.MIMEText import MIMEText -from email.Utils import formatdate -from email.header import decode_header from twisted.application.service import Service, IService from twisted.internet import inotify, defer, task, reactor @@ -52,88 +48,15 @@ from twisted.python import filepath, log from zope.interface import implements -from leap.soledad.common.crypto import ( - EncryptionSchemes, - ENC_JSON_KEY, - ENC_SCHEME_KEY, -) +from leap.soledad.common.crypto import EncryptionSchemes +from leap.soledad.common.crypto import ENC_JSON_KEY +from leap.soledad.common.crypto import ENC_SCHEME_KEY from leap.soledad.common.couch import CouchDatabase, CouchDocument -from leap.keymanager import openpgp - -BOUNCE_TEMPLATE = """ -Delivery to the following recipient failed: - {0} - -Reasons: - {1} - -Original message: - {2} -""".strip() - - -from twisted.internet import protocol -from twisted.internet.error import ProcessDone - - -class BouncerSubprocessProtocol(protocol.ProcessProtocol): - """ - Bouncer subprocess protocol that will feed the msg contents to be - bounced through stdin - """ - - def __init__(self, msg): - """ - Constructor for the BouncerSubprocessProtocol - - :param msg: Message to send to stdin when the process has - launched - :type msg: str - """ - self._msg = msg - self._outBuffer = "" - self._errBuffer = "" - self._d = None - - @property - def deferred(self): - return self._d - - def connectionMade(self): - self._d = defer.Deferred() - self.transport.write(self._msg) - self.transport.closeStdin() - - def outReceived(self, data): - self._outBuffer += data - - def errReceived(self, data): - self._errBuffer += data - - def processEnded(self, reason): - if reason.check(ProcessDone): - self._d.callback(self._outBuffer) - else: - self._d.errback(reason) - - -def async_check_output(args, msg): - """ - Async spawn a process and return a defer to be able to check the - output with a callback/errback - - :param args: the command to execute along with the params for it - :type args: list of str - :param msg: string that will be send to stdin of the process once - it's spawned - :type msg: str +from leap.keymanager import openpgp - :rtype: defer.Deferred - """ - pprotocol = BouncerSubprocessProtocol(msg) - reactor.spawnProcess(pprotocol, args[0], args) - return pprotocol.deferred +from leap.mx.bounce import bounce_message +from leap.mx.bounce import InvalidReturnPathError class MailReceiver(Service): @@ -376,10 +299,10 @@ class MailReceiver(Service): return uuid @defer.inlineCallbacks - def _bounce_mail(self, orig_msg, filepath, reason): + def _bounce_message(self, orig_msg, filepath, reason): """ - Bounces the email contained in orig_msg to it's sender and - removes it from the queue. + Bounce the message contained in orig_msg to it's sender and + remove it from the queue. :param orig_msg: Message that is going to be bounced :type orig_msg: email.message.Message @@ -388,23 +311,12 @@ class MailReceiver(Service): :param reason: Brief explanation about why it's being bounced :type reason: str """ - orig_from = orig_msg.get("From") - orig_to = orig_msg.get("To") - - msg = MIMEMultipart() - msg['From'] = self._bounce_from - msg['To'] = orig_from - msg['Date'] = formatdate(localtime=True) - msg['Subject'] = self._bounce_subject - - decoded_to = " ".join([x[0] for x in decode_header(orig_to)]) - text = BOUNCE_TEMPLATE.format(decoded_to, - reason, - orig_msg.as_string()) - - msg.attach(MIMEText(text)) - - yield async_check_output(["/usr/sbin/sendmail", "-t"], msg.as_string()) + try: + yield bounce_message( + self._bounce_from, self._bounce_subject, orig_msg, reason) + except InvalidReturnPathError: + # give up bouncing this message! + log.msg("Will not bounce message because of invalid return path.") yield self._conditional_remove(True, filepath) def sleep(self, secs): @@ -479,7 +391,7 @@ class MailReceiver(Service): (filepath.path,)) bounce_reason = "Missing UUID: There was a problem " \ "locating the user in our database." - yield self._bounce_mail(msg, filepath, bounce_reason) + yield self._bounce_message(msg, filepath, bounce_reason) defer.returnValue(None) log.msg("Mail owner: %s" % (uuid,)) @@ -489,11 +401,13 @@ class MailReceiver(Service): pubkey = yield self._users_cdb.getPubkey(uuid) if pubkey is None or len(pubkey) == 0: - log.msg("No public key, stopping the processing chain") - bounce_reason = "Missing PubKey: There was a problem " \ - "locating the user's public key in our " \ - "database." - yield self._bounce_mail(msg, filepath, bounce_reason) + log.msg( + "No public key for %s, stopping the processing chain." + % uuid) + bounce_reason = "Missing PGP public key: There was a " \ + "problem locating the user's public key in " \ + "our database." + yield self._bounce_message(msg, filepath, bounce_reason) defer.returnValue(None) log.msg("Encrypting message to %s's pubkey" % (uuid,)) -- cgit v1.2.3