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 | 
