diff options
-rw-r--r-- | changes/bug_3937_fix_ui_freeze | 1 | ||||
-rw-r--r-- | changes/bug_4441_fix-fqdn | 1 | ||||
-rw-r--r-- | src/leap/mail/imap/server.py | 1 | ||||
-rw-r--r-- | src/leap/mail/smtp/gateway.py | 107 |
4 files changed, 79 insertions, 31 deletions
diff --git a/changes/bug_3937_fix_ui_freeze b/changes/bug_3937_fix_ui_freeze new file mode 100644 index 0000000..b91938c --- /dev/null +++ b/changes/bug_3937_fix_ui_freeze @@ -0,0 +1 @@ + o Uses deferToThread for sendMail. Closes #3937 diff --git a/changes/bug_4441_fix-fqdn b/changes/bug_4441_fix-fqdn new file mode 100644 index 0000000..e758d65 --- /dev/null +++ b/changes/bug_4441_fix-fqdn @@ -0,0 +1 @@ + o Identify ourselves with a fqdn, always. Closes: #4441 diff --git a/src/leap/mail/imap/server.py b/src/leap/mail/imap/server.py index 7a9f810..11f3ccf 100644 --- a/src/leap/mail/imap/server.py +++ b/src/leap/mail/imap/server.py @@ -1153,7 +1153,6 @@ class SoledadMailbox(WithMsgFields): # the server itself is a listener to the mailbox. # so we can notify it (and should!) after chanes in flags # and number of messages. - print "emptying the listeners" map(lambda i: self.listeners.remove(i), self.listeners) def addListener(self, listener): diff --git a/src/leap/mail/smtp/gateway.py b/src/leap/mail/smtp/gateway.py index 06405b4..f6366af 100644 --- a/src/leap/mail/smtp/gateway.py +++ b/src/leap/mail/smtp/gateway.py @@ -14,7 +14,6 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. - """ LEAP SMTP encrypted gateway. @@ -32,7 +31,6 @@ The following classes comprise the SMTP gateway service: """ - import re from StringIO import StringIO from email.Header import Header @@ -46,6 +44,7 @@ from twisted.mail import smtp from twisted.internet.protocol import ServerFactory from twisted.internet import reactor, ssl from twisted.internet import defer +from twisted.internet.threads import deferToThread from twisted.python import log from leap.common.check import leap_assert, leap_assert_type @@ -71,6 +70,9 @@ generator.Generator = RFC3156CompliantGenerator # Helper utilities # +LOCAL_FQDN = "bitmask.local" + + def validate_address(address): """ Validate C{address} as defined in RFC 2822. @@ -96,10 +98,25 @@ def validate_address(address): # SMTPFactory # +class SMTPHeloLocalhost(smtp.SMTP): + """ + An SMTP class that ensures a proper FQDN + for localhost. + + This avoids a problem in which unproperly configured providers + would complain about the helo not being a fqdn. + """ + + def __init__(self, *args): + smtp.SMTP.__init__(self, *args) + self.host = LOCAL_FQDN + + class SMTPFactory(ServerFactory): """ Factory for an SMTP server with encrypted gatewaying capabilities. """ + domain = LOCAL_FQDN def __init__(self, userid, keymanager, host, port, cert, key, encrypted_only): @@ -152,7 +169,7 @@ class SMTPFactory(ServerFactory): @return: The protocol. @rtype: SMTPDelivery """ - smtpProtocol = smtp.SMTP(SMTPDelivery( + smtpProtocol = SMTPHeloLocalhost(SMTPDelivery( self._userid, self._km, self._host, self._port, self._cert, self._key, self._encrypted_only)) smtpProtocol.factory = self @@ -379,21 +396,14 @@ class EncryptedMessage(object): Handle end of message. This method will encrypt and send the message. + + :returns: a deferred """ log.msg("Message data complete.") self.lines.append('') # add a trailing newline - try: - self._maybe_encrypt_and_sign() - return self.sendMessage() - except KeyNotFound: - return None - - def parseMessage(self): - """ - Separate message headers from body. - """ - parser = Parser() - return parser.parsestr('\r\n'.join(self.lines)) + d = deferToThread(self._maybe_encrypt_and_sign) + d.addCallbacks(self.sendMessage, self.skipNoKeyErrBack) + return d def connectionLost(self): """ @@ -405,6 +415,37 @@ class EncryptedMessage(object): # unexpected loss of connection; don't save self.lines = [] + # ends IMessage implementation + + def skipNoKeyErrBack(self, failure): + """ + Errback that ignores a KeyNotFound + + :param failure: the failure + :type Failure: Failure + """ + err = failure.value + if failure.check(KeyNotFound): + pass + else: + raise err + + def parseMessage(self): + """ + Separate message headers from body. + """ + parser = Parser() + return parser.parsestr('\r\n'.join(self.lines)) + + def sendQueued(self, r): + """ + Callback for the queued message. + + :param r: The result from the last previous callback in the chain. + :type r: anything + """ + log.msg(r) + def sendSuccess(self, r): """ Callback for a successful send. @@ -415,33 +456,40 @@ class EncryptedMessage(object): log.msg(r) signal(proto.SMTP_SEND_MESSAGE_SUCCESS, self._user.dest.addrstr) - def sendError(self, e): + def sendError(self, failure): """ Callback for an unsuccessfull send. :param e: The result from the last errback. :type e: anything """ - log.msg(e) - log.err() signal(proto.SMTP_SEND_MESSAGE_ERROR, self._user.dest.addrstr) + err = failure.value + log.err(err) + raise err - def sendMessage(self): + def sendMessage(self, *args): """ - Send the message. - - This method will prepare the message (headers and possibly encrypted - body) and send it using the ESMTPSenderFactory. + Sends the message. - @return: A deferred with callbacks for error and success of this - message send. - @rtype: twisted.internet.defer.Deferred + :return: A deferred with callbacks for error and success of this + #message send. + :rtype: twisted.internet.defer.Deferred """ - msg = self._msg.as_string(False) + d = deferToThread(self._route_msg) + d.addCallbacks(self.sendQueued, self.sendError) + return + def _route_msg(self): + """ + Sends the msg using the ESMTPSenderFactory. + """ log.msg("Connecting to SMTP server %s:%s" % (self._host, self._port)) + msg = self._msg.as_string(False) + # we construct a defer to pass to the ESMTPSenderFactory d = defer.Deferred() + d.addCallbacks(self.sendSuccess, self.sendError) # we don't pass an ssl context factory to the ESMTPSenderFactory # because ssl will be handled by reactor.connectSSL() below. factory = smtp.ESMTPSenderFactory( @@ -451,15 +499,14 @@ class EncryptedMessage(object): self._user.dest.addrstr, StringIO(msg), d, + heloFallback=True, requireAuthentication=False, requireTransportSecurity=True) + factory.domain = LOCAL_FQDN signal(proto.SMTP_SEND_MESSAGE_START, self._user.dest.addrstr) reactor.connectSSL( self._host, self._port, factory, contextFactory=SSLContextFactory(self._cert, self._key)) - d.addCallback(self.sendSuccess) - d.addErrback(self.sendError) - return d # # encryption methods |