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(-) 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 61620db0f917bc5ad79f17d928b80d739747fcf5 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 8 Jan 2015 20:53:08 -0400 Subject: log to syslog. Closes: #6307 --- changes/feature_6307-log-to-syslog | 1 + pkg/leap_mx | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 changes/feature_6307-log-to-syslog diff --git a/changes/feature_6307-log-to-syslog b/changes/feature_6307-log-to-syslog new file mode 100644 index 0000000..a557fd9 --- /dev/null +++ b/changes/feature_6307-log-to-syslog @@ -0,0 +1 @@ +o Log to syslog. Closes: #6307 diff --git a/pkg/leap_mx b/pkg/leap_mx index da8a3c8..c08adb0 100644 --- a/pkg/leap_mx +++ b/pkg/leap_mx @@ -21,7 +21,8 @@ case "$1" in --pidfile=$pidfile \ --rundir=$rundir \ --python=$file \ - --logfile=$logfile + --logfile=$logfile \ + --syslog echo "." ;; -- cgit v1.2.3 From deeaa37a64b842ae6a6f05b8748c536df72468ba Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Mon, 12 Jan 2015 11:58:52 -0500 Subject: add --prefix argument to get distinguishable syslog messages --- pkg/leap_mx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/leap_mx b/pkg/leap_mx index c08adb0..a5c7438 100644 --- a/pkg/leap_mx +++ b/pkg/leap_mx @@ -22,7 +22,7 @@ case "$1" in --rundir=$rundir \ --python=$file \ --logfile=$logfile \ - --syslog + --syslog --prefix=leap-mx echo "." ;; -- cgit v1.2.3 From 937227d7c84de2cafe968525e6f889ddf102062f Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Mon, 12 Jan 2015 12:40:44 -0500 Subject: sync initscript with debian version --- pkg/leap_mx | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/pkg/leap_mx b/pkg/leap_mx index a5c7438..60dddc4 100644 --- a/pkg/leap_mx +++ b/pkg/leap_mx @@ -1,14 +1,25 @@ #!/bin/sh +### BEGIN INIT INFO +# Provides: leap-mx +# Required-Start: $network $named $remote_fs $syslog $time +# Required-Stop: $network $named $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start leap-mx daemon at boot time +# Description: transparently-encrypting remailer +### END INIT INFO PATH=/sbin:/bin:/usr/sbin:/usr/bin - -pidfile=/var/run/leap_mx.pid \ -rundir=/var/lib/leap_mx/ \ -file=/usr/local/bin/mx.tac \ -logfile=/var/log/leap_mx.log +PIDFILE=/var/run/leap_mx.pid +RUNDIR=/var/lib/leap_mx/ +FILE=/usr/share/app/leap_mx.tac +LOGFILE=/var/log/leap_mx.log +TWISTD_PATH=/usr/bin/twistd [ -r /etc/default/leap_mx ] && . /etc/default/leap_mx +. /lib/lsb/init-functions + test -r $file || exit 0 test -r /etc/leap/ || exit 0 @@ -17,11 +28,11 @@ case "$1" in start) echo -n "Starting leap_mx: twistd" HOME="/var/lib/leap_mx/" \ - start-stop-daemon --start --quiet --exec /usr/local/bin/twistd -- \ - --pidfile=$pidfile \ - --rundir=$rundir \ - --python=$file \ - --logfile=$logfile \ + start-stop-daemon --start --quiet --exec $TWISTD_PATH -- \ + --pidfile=$PIDFILE \ + --rundir=$RUNDIR \ + --python=$FILE \ + --logfile=$LOGFILE \ --syslog --prefix=leap-mx echo "." ;; @@ -29,7 +40,7 @@ case "$1" in stop) echo -n "Stopping leap_mx: twistd" start-stop-daemon --stop --quiet \ - --pidfile $pidfile + --pidfile $PIDFILE echo "." ;; @@ -42,8 +53,12 @@ case "$1" in $0 restart ;; + status) + status_of_proc -p $PIDFILE $TWISTD_PATH leap_mx && exit 0 || exit $? + ;; + *) - echo "Usage: /etc/init.d/leap_mx {start|stop|restart|force-reload}" >&2 + echo "Usage: /etc/init.d/leap_mx {start|stop|restart|force-reload|status}" >&2 exit 1 ;; esac -- 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). --- ...ature_2591_process-unprocessed-mail-when-mx-starts | 1 + src/leap/mx/mail_receiver.py | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 changes/feature_2591_process-unprocessed-mail-when-mx-starts diff --git a/changes/feature_2591_process-unprocessed-mail-when-mx-starts b/changes/feature_2591_process-unprocessed-mail-when-mx-starts new file mode 100644 index 0000000..fd5ceb0 --- /dev/null +++ b/changes/feature_2591_process-unprocessed-mail-when-mx-starts @@ -0,0 +1 @@ +o Process unprocessed mail when MX starts (closes #2591). 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(-) 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). --- ...6687_log-and-retry-when-watched-dirs-dont-exist | 1 + src/leap/mx/mail_receiver.py | 68 +++++++++++++++++----- 2 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 changes/bug_6687_log-and-retry-when-watched-dirs-dont-exist diff --git a/changes/bug_6687_log-and-retry-when-watched-dirs-dont-exist b/changes/bug_6687_log-and-retry-when-watched-dirs-dont-exist new file mode 100644 index 0000000..bddba01 --- /dev/null +++ b/changes/bug_6687_log-and-retry-when-watched-dirs-dont-exist @@ -0,0 +1 @@ + o MX log and retry when delivery directories don't exist. Closes #6687. 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 b5ed3e4db16c8e3856b5c45409807d8f58cd957a Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 11 Feb 2015 12:17:52 -0300 Subject: Fold in changes --- CHANGELOG | 5 +++++ changes/bug_6687_log-and-retry-when-watched-dirs-dont-exist | 1 - changes/feature_2591_process-unprocessed-mail-when-mx-starts | 1 - changes/feature_6307-log-to-syslog | 1 - 4 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 changes/bug_6687_log-and-retry-when-watched-dirs-dont-exist delete mode 100644 changes/feature_2591_process-unprocessed-mail-when-mx-starts delete mode 100644 changes/feature_6307-log-to-syslog diff --git a/CHANGELOG b/CHANGELOG index 87f6ef1..6f58b5e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +0.6.1 Feb 11, 2015: + o Process unprocessed mail when MX starts (closes #2591). + o Log to syslog. Closes: #6307 + o MX log and retry when delivery directories don't exist. Closes #6687. + 0.6.0 Sept 26, 2014: o Bounce mails when there's a problematic situation for an email, such as no public key. Fixes #4803. diff --git a/changes/bug_6687_log-and-retry-when-watched-dirs-dont-exist b/changes/bug_6687_log-and-retry-when-watched-dirs-dont-exist deleted file mode 100644 index bddba01..0000000 --- a/changes/bug_6687_log-and-retry-when-watched-dirs-dont-exist +++ /dev/null @@ -1 +0,0 @@ - o MX log and retry when delivery directories don't exist. Closes #6687. diff --git a/changes/feature_2591_process-unprocessed-mail-when-mx-starts b/changes/feature_2591_process-unprocessed-mail-when-mx-starts deleted file mode 100644 index fd5ceb0..0000000 --- a/changes/feature_2591_process-unprocessed-mail-when-mx-starts +++ /dev/null @@ -1 +0,0 @@ -o Process unprocessed mail when MX starts (closes #2591). diff --git a/changes/feature_6307-log-to-syslog b/changes/feature_6307-log-to-syslog deleted file mode 100644 index a557fd9..0000000 --- a/changes/feature_6307-log-to-syslog +++ /dev/null @@ -1 +0,0 @@ -o Log to syslog. Closes: #6307 -- 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 --- changes/bug_correctly-return-async-bouncer-deferred | 1 + src/leap/mx/mail_receiver.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 changes/bug_correctly-return-async-bouncer-deferred diff --git a/changes/bug_correctly-return-async-bouncer-deferred b/changes/bug_correctly-return-async-bouncer-deferred new file mode 100644 index 0000000..15d3691 --- /dev/null +++ b/changes/bug_correctly-return-async-bouncer-deferred @@ -0,0 +1 @@ + o Correctly return async bouncer deferred. 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 5a45acd3486f4e7f830647953731353cda916d51 Mon Sep 17 00:00:00 2001 From: drebs Date: Tue, 24 Mar 2015 15:09:18 -0300 Subject: [feat] reject incoming mail if no pgp key found Implement a PGP key lookup in the postfix smtp recipient restriction and virtual alias mapping levels. If no PGP key is found, then the address is rejected with a temporary error. Closes: #6795 --- changes/bug_6795_reject-mail-if-no-pgp-key-found | 1 + src/leap/mx/alias_resolver.py | 45 +++++++++++------------- src/leap/mx/check_recipient_access.py | 5 ++- 3 files changed, 26 insertions(+), 25 deletions(-) create mode 100644 changes/bug_6795_reject-mail-if-no-pgp-key-found diff --git a/changes/bug_6795_reject-mail-if-no-pgp-key-found b/changes/bug_6795_reject-mail-if-no-pgp-key-found new file mode 100644 index 0000000..7b9ef1f --- /dev/null +++ b/changes/bug_6795_reject-mail-if-no-pgp-key-found @@ -0,0 +1 @@ + o Reject mail if no PGP key was found for a user. Closes #6795. diff --git a/src/leap/mx/alias_resolver.py b/src/leap/mx/alias_resolver.py index 45a3ed2..4247b57 100644 --- a/src/leap/mx/alias_resolver.py +++ b/src/leap/mx/alias_resolver.py @@ -28,10 +28,11 @@ TODO: try: # TODO: we should probably use the system alias somehow - # from twisted.mail import alias + # from twisted.mail import alias from twisted.protocols import postfix from twisted.python import log from twisted.internet import defer + from twisted.internet.protocol import ServerFactory except ImportError: print "This software requires Twisted. Please see the README file" print "for instructions on getting required dependencies." @@ -39,19 +40,20 @@ except ImportError: class LEAPPostFixTCPMapserver(postfix.PostfixTCPMapServer): def _cbGot(self, value): - if value is None: + uuid, pubkey = value + if uuid is None: self.sendCode(500, postfix.quote("NOT FOUND SRY")) + elif pubkey is None: + self.sendCode(400, postfix.quote("4.7.13 USER ACCOUNT DISABLED")) else: self.sendCode(200, postfix.quote(value)) -class AliasResolverFactory(postfix.PostfixTCPMapDeferringDictServerFactory): +class AliasResolverFactory(ServerFactory): protocol = LEAPPostFixTCPMapserver - def __init__(self, couchdb, *args, **kwargs): - postfix.PostfixTCPMapDeferringDictServerFactory.__init__( - self, *args, **kwargs) + def __init__(self, couchdb): self._cdb = couchdb def _to_str(self, result): @@ -64,14 +66,14 @@ class AliasResolverFactory(postfix.PostfixTCPMapDeferringDictServerFactory): log.msg("Result not found") return result - def spit_result(self, result): - """ - Formats the return codes in a postfix friendly format. - """ - if result is None: - return None - else: - return defer.succeed(result) + def _getPubKey(self, uuid): + if uuid is None: + return defer.succeed([None, None]) + d = defer.gatherResults([ + self._to_str(uuid), + self._cdb.getPubKey(uuid), + ]) + return d def get(self, key): """ @@ -79,13 +81,8 @@ class AliasResolverFactory(postfix.PostfixTCPMapDeferringDictServerFactory): At some point we will have to consider the domain part too. """ - try: - log.msg("Query key: %s" % (key,)) - d = self._cdb.queryByAddress(key) - - d.addCallback(self._to_str) - d.addCallback(self.spit_result) - d.addErrback(log.err) - return d - except Exception as e: - log.err('exception in get: %r' % e) + log.msg("Query key: %s" % (key,)) + d = self._cdb.queryByAddress(key) + d.addCallback(self._getPubKey) + d.addErrback(log.err) + return d diff --git a/src/leap/mx/check_recipient_access.py b/src/leap/mx/check_recipient_access.py index b80ccfd..d4ae339 100644 --- a/src/leap/mx/check_recipient_access.py +++ b/src/leap/mx/check_recipient_access.py @@ -32,8 +32,11 @@ class LEAPPostFixTCPMapserverAccess(postfix.PostfixTCPMapServer): # For more info, see: # http://www.postfix.org/tcp_table.5.html # http://www.postfix.org/access.5.html - if value is None: + uuid, pubkey = value + if uuid is None: self.sendCode(500, postfix.quote("REJECT")) + elif pubkey is None: + self.sendCode(400, postfix.quote("4.7.13 USER ACCOUNT DISABLED")) else: self.sendCode(200, postfix.quote("OK")) -- cgit v1.2.3 From b5309dc5910f35f5320c649be2ce2c6147030b39 Mon Sep 17 00:00:00 2001 From: drebs Date: Wed, 25 Mar 2015 15:47:28 -0300 Subject: [refactor] separate tcp map server code Separate the common tcp map server code, used for both alias resolver and recipient access checker, to its own file. --- src/leap/mx/alias_resolver.py | 84 ++++++++++++++--------------------- src/leap/mx/check_recipient_access.py | 40 ++++++++++++----- src/leap/mx/tcp_map.py | 76 +++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 60 deletions(-) create mode 100644 src/leap/mx/tcp_map.py diff --git a/src/leap/mx/alias_resolver.py b/src/leap/mx/alias_resolver.py index 4247b57..9206ffb 100644 --- a/src/leap/mx/alias_resolver.py +++ b/src/leap/mx/alias_resolver.py @@ -24,65 +24,49 @@ Test this with postmap -v -q "foo" tcp:localhost:4242 TODO: o Look into using twisted.protocols.postfix.policies classes for controlling concurrent connections and throttling resource consumption. + o We should probably use twisted.mail.alias somehow. """ -try: - # TODO: we should probably use the system alias somehow - # from twisted.mail import alias - from twisted.protocols import postfix - from twisted.python import log - from twisted.internet import defer - from twisted.internet.protocol import ServerFactory -except ImportError: - print "This software requires Twisted. Please see the README file" - print "for instructions on getting required dependencies." +from twisted.protocols import postfix -class LEAPPostFixTCPMapserver(postfix.PostfixTCPMapServer): - def _cbGot(self, value): - uuid, pubkey = value - if uuid is None: - self.sendCode(500, postfix.quote("NOT FOUND SRY")) - elif pubkey is None: - self.sendCode(400, postfix.quote("4.7.13 USER ACCOUNT DISABLED")) - else: - self.sendCode(200, postfix.quote(value)) - +from leap.mx.tcp_map import LEAPostfixTCPMapServerFactory +from leap.mx.tcp_map import TCP_MAP_CODE_SUCCESS +from leap.mx.tcp_map import TCP_MAP_CODE_TEMPORARY_FAILURE +from leap.mx.tcp_map import TCP_MAP_CODE_PERMANENT_FAILURE -class AliasResolverFactory(ServerFactory): - protocol = LEAPPostFixTCPMapserver +class LEAPPostfixTCPMapAliasServer(postfix.PostfixTCPMapServer): + """ + A postfix tcp map alias resolver server. + """ - def __init__(self, couchdb): - self._cdb = couchdb - - def _to_str(self, result): - """ - Properly encodes the result string if any. + def _cbGot(self, value): """ - if isinstance(result, unicode): - result = result.encode("utf8") - if result is None: - log.msg("Result not found") - return result + Return a code and message depending on the result of the factory's + get(). - def _getPubKey(self, uuid): + :param value: The uuid and public key. + :type value: list + """ + uuid, pubkey = value if uuid is None: - return defer.succeed([None, None]) - d = defer.gatherResults([ - self._to_str(uuid), - self._cdb.getPubKey(uuid), - ]) - return d + self.sendCode( + TCP_MAP_CODE_PERMANENT_FAILURE, + postfix.quote("NOT FOUND SRY")) + elif pubkey is None: + self.sendCode( + TCP_MAP_CODE_TEMPORARY_FAILURE, + postfix.quote("4.7.13 USER ACCOUNT DISABLED")) + else: + self.sendCode( + TCP_MAP_CODE_SUCCESS, + postfix.quote(uuid)) - def get(self, key): - """ - Looks up the passed key, but only up to the username id of the key. - At some point we will have to consider the domain part too. - """ - log.msg("Query key: %s" % (key,)) - d = self._cdb.queryByAddress(key) - d.addCallback(self._getPubKey) - d.addErrback(log.err) - return d +class AliasResolverFactory(LEAPostfixTCPMapServerFactory): + """ + A factory for postfix tcp map alias resolver servers. + """ + + protocol = LEAPPostfixTCPMapAliasServer diff --git a/src/leap/mx/check_recipient_access.py b/src/leap/mx/check_recipient_access.py index d4ae339..cf172c7 100644 --- a/src/leap/mx/check_recipient_access.py +++ b/src/leap/mx/check_recipient_access.py @@ -24,22 +24,42 @@ Test this with postmap -v -q "foo" tcp:localhost:2244 from twisted.protocols import postfix -from leap.mx.alias_resolver import AliasResolverFactory +from leap.mx.tcp_map import LEAPPostfixTCPMapServerFactory +from leap.mx.tcp_map import TCP_MAP_CODE_SUCCESS +from leap.mx.tcp_map import TCP_MAP_CODE_TEMPORARY_FAILURE +from leap.mx.tcp_map import TCP_MAP_CODE_PERMANENT_FAILURE -class LEAPPostFixTCPMapserverAccess(postfix.PostfixTCPMapServer): +class LEAPPostFixTCPMapAccessServer(postfix.PostfixTCPMapServer): + """ + A postfix tcp map recipient access checker server. + """ + def _cbGot(self, value): - # For more info, see: - # http://www.postfix.org/tcp_table.5.html - # http://www.postfix.org/access.5.html + """ + Return a code and message depending on the result of the factory's + get(). + + For more info, see: http://www.postfix.org/access.5.html + + :param value: The uuid and public key. + :type value: list + """ uuid, pubkey = value if uuid is None: - self.sendCode(500, postfix.quote("REJECT")) + self.sendCode( + TCP_MAP_CODE_PERMANENT_FAILURE, + postfix.quote("REJECT")) elif pubkey is None: - self.sendCode(400, postfix.quote("4.7.13 USER ACCOUNT DISABLED")) + self.sendCode( + TCP_MAP_CODE_TEMPORARY_FAILURE, + postfix.quote("4.7.13 USER ACCOUNT DISABLED")) else: - self.sendCode(200, postfix.quote("OK")) + self.sendCode( + TCP_MAP_CODE_SUCCESS, + postfix.quote("OK")) + +class CheckRecipientAccessFactory(LEAPPostfixTCPMapServerFactory): -class CheckRecipientAccessFactory(AliasResolverFactory): - protocol = LEAPPostFixTCPMapserverAccess + protocol = LEAPPostFixTCPMapAccessServer diff --git a/src/leap/mx/tcp_map.py b/src/leap/mx/tcp_map.py new file mode 100644 index 0000000..b7066ff --- /dev/null +++ b/src/leap/mx/tcp_map.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- +# tcpmap.py +# Copyright (C) 2015 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 . + + +from twisted.python import log +from twisted.internet import defer +from twisted.internet.protocol import ServerFactory + + +# For info on codes, see: http://www.postfix.org/tcp_table.5.html +TCP_MAP_CODE_SUCCESS = 200 +TCP_MAP_CODE_TEMPORARY_FAILURE = 400 +TCP_MAP_CODE_PERMANENT_FAILURE = 500 + + +class LEAPPostfixTCPMapServerFactory(ServerFactory): + """ + A factory for postfix tcp map servers. + """ + + def __init__(self, couchdb): + """ + Initialize the factory. + + :param couchdb: A CouchDB client. + :type couchdb: leap.mx.couchdbhelper.ConnectedCouchDB + """ + self._cdb = couchdb + + def _getPubKey(self, uuid): + """ + Look up PGP public key based on user uid. + + :param uuid: The user uid. + :type uuid: str + + :return: A deferred that is fired with the uuid and the public key, if + available. + :rtype: DeferredList + """ + if uuid is None: + return defer.succeed([None, None]) + return defer.gatherResults([ + defer.succeed(uuid), + self._cdb.getPubKey(uuid), + ]) + + def get(self, key): + """ + Look up uuid based on key, only up to the username id of the key. + + At some point we will have to consider the domain part too. + + :param key: The lookup key. + :type key: str + """ + log.msg("Query key: %s" % (key,)) + d = self._cdb.queryByAddress(key) + d.addCallback(self._getPubKey) + d.addErrback(log.err) + return d -- cgit v1.2.3 From e8fd9feb5891b6cd0840afdcae996314ea3849a9 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 26 Mar 2015 10:44:18 -0400 Subject: [docs] add leap standard commit template to repo Because in CDO we trust. --- doc/leap-commit-template | 7 ++++++ doc/leap-commit-template.README | 47 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 doc/leap-commit-template create mode 100644 doc/leap-commit-template.README diff --git a/doc/leap-commit-template b/doc/leap-commit-template new file mode 100644 index 0000000..8a5c7cd --- /dev/null +++ b/doc/leap-commit-template @@ -0,0 +1,7 @@ +[bug|feat|docs|style|refactor|test|pkg|i18n] ... +... + +- Resolves: #XYZ +- Related: #XYZ +- Documentation: #XYZ +- Releases: XYZ diff --git a/doc/leap-commit-template.README b/doc/leap-commit-template.README new file mode 100644 index 0000000..ce8809e --- /dev/null +++ b/doc/leap-commit-template.README @@ -0,0 +1,47 @@ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +HOW TO USE THIS TEMPLATE: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Run `git config commit.template docs/leap-commit-template` or +edit the .git/config for this project and add +`template = docs/leap-commit-template` +under the [commit] block + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +COMMIT TEMPLATE FORMAT EXPLAINED +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +[type] + + +