summaryrefslogtreecommitdiff
path: root/src/leap/mail/smtp
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/mail/smtp')
-rw-r--r--src/leap/mail/smtp/__init__.py6
-rw-r--r--src/leap/mail/smtp/gateway.py16
-rw-r--r--src/leap/mail/smtp/rfc3156.py31
-rw-r--r--src/leap/mail/smtp/tests/test_gateway.py60
4 files changed, 75 insertions, 38 deletions
diff --git a/src/leap/mail/smtp/__init__.py b/src/leap/mail/smtp/__init__.py
index d3eb9e8..bbd4064 100644
--- a/src/leap/mail/smtp/__init__.py
+++ b/src/leap/mail/smtp/__init__.py
@@ -30,7 +30,7 @@ from leap.mail.smtp.gateway import SMTPFactory
def setup_smtp_gateway(port, userid, keymanager, smtp_host, smtp_port,
- smtp_cert, smtp_key, encrypted_only):
+ smtp_cert, smtp_key, encrypted_only):
"""
Setup SMTP gateway to run with Twisted.
@@ -52,8 +52,8 @@ def setup_smtp_gateway(port, userid, keymanager, smtp_host, smtp_port,
:type smtp_cert: str
:param smtp_key: The client key for authentication.
:type smtp_key: str
- :param encrypted_only: Whether the SMTP gateway should send unencrypted mail
- or not.
+ :param encrypted_only: Whether the SMTP gateway should send unencrypted
+ mail or not.
:type encrypted_only: bool
:returns: tuple of SMTPFactory, twisted.internet.tcp.Port
diff --git a/src/leap/mail/smtp/gateway.py b/src/leap/mail/smtp/gateway.py
index a78bd55..ef398d1 100644
--- a/src/leap/mail/smtp/gateway.py
+++ b/src/leap/mail/smtp/gateway.py
@@ -52,6 +52,7 @@ from leap.common.events import proto, signal
from leap.keymanager import KeyManager
from leap.keymanager.openpgp import OpenPGPKey
from leap.keymanager.errors import KeyNotFound
+from leap.mail import __version__
from leap.mail.smtp.rfc3156 import (
MultipartSigned,
MultipartEncrypted,
@@ -492,7 +493,7 @@ class EncryptedMessage(object):
heloFallback=True,
requireAuthentication=False,
requireTransportSecurity=True)
- factory.domain = LOCAL_FQDN
+ factory.domain = __version__
signal(proto.SMTP_SEND_MESSAGE_START, self._user.dest.addrstr)
reactor.connectSSL(
self._host, self._port, factory,
@@ -599,13 +600,16 @@ class EncryptedMessage(object):
self._msg = self._origmsg
return
- # add a nice footer to the outgoing message
from_address = validate_address(self._fromAddress.addrstr)
username, domain = from_address.split('@')
- self.lines.append('--')
- self.lines.append('%s - https://%s/key/%s.' %
- (self.FOOTER_STRING, domain, username))
- self.lines.append('')
+
+ # add a nice footer to the outgoing message
+ if self._origmsg.get_content_type() == 'text/plain':
+ self.lines.append('--')
+ self.lines.append('%s - https://%s/key/%s' %
+ (self.FOOTER_STRING, domain, username))
+ self.lines.append('')
+
self._origmsg = self.parseMessage()
# get sender and recipient data
diff --git a/src/leap/mail/smtp/rfc3156.py b/src/leap/mail/smtp/rfc3156.py
index dd48475..62a0675 100644
--- a/src/leap/mail/smtp/rfc3156.py
+++ b/src/leap/mail/smtp/rfc3156.py
@@ -24,6 +24,7 @@ import base64
from abc import ABCMeta, abstractmethod
from StringIO import StringIO
+from twisted.python import log
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email import errors
@@ -145,14 +146,26 @@ def encode_base64(msg):
:param msg: The non-multipart message to be encoded.
:type msg: email.message.Message
"""
- orig = msg.get_payload()
- encdata = _bencode(orig)
- msg.set_payload(encdata)
- # replace or set the Content-Transfer-Encoding header.
- try:
- msg.replace_header('Content-Transfer-Encoding', 'base64')
- except KeyError:
- msg['Content-Transfer-Encoding'] = 'base64'
+ encoding = msg.get('Content-Transfer-Encoding', None)
+ if encoding is not None:
+ encoding = encoding.lower()
+ # XXX Python's email module can only decode quoted-printable, base64 and
+ # uuencoded data, so we might have to implement other decoding schemes in
+ # order to support RFC 3156 properly and correctly calculate signatures
+ # for multipart attachments (eg. 7bit or 8bit encoded attachments). For
+ # now, if content is already encoded as base64 or if it is encoded with
+ # some unknown encoding, we just pass.
+ if encoding in [None, 'quoted-printable', 'x-uuencode', 'uue', 'x-uue']:
+ orig = msg.get_payload(decode=True)
+ encdata = _bencode(orig)
+ msg.set_payload(encdata)
+ # replace or set the Content-Transfer-Encoding header.
+ try:
+ msg.replace_header('Content-Transfer-Encoding', 'base64')
+ except KeyError:
+ msg['Content-Transfer-Encoding'] = 'base64'
+ elif encoding is not 'base64':
+ log.err('Unknown content-transfer-encoding: %s' % encoding)
def encode_base64_rec(msg):
@@ -361,7 +374,7 @@ class PGPSignature(MIMEApplication):
"""
def __init__(self, _data, name='signature.asc'):
MIMEApplication.__init__(self, _data, 'pgp-signature',
- _encoder=lambda x: x, name=name)
+ _encoder=lambda x: x, name=name)
self.add_header('Content-Description', 'OpenPGP Digital Signature')
diff --git a/src/leap/mail/smtp/tests/test_gateway.py b/src/leap/mail/smtp/tests/test_gateway.py
index 4c2f04f..88ee5f7 100644
--- a/src/leap/mail/smtp/tests/test_gateway.py
+++ b/src/leap/mail/smtp/tests/test_gateway.py
@@ -101,10 +101,16 @@ class TestSmtpGateway(TestCaseWithKeyManager):
'250 Sender address accepted',
'250 Recipient address accepted',
'354 Continue']
- proto = SMTPFactory(u'anotheruser@leap.se',
- self._km, self._config['host'], self._config['port'],
+
+ # XXX this bit can be refactored away in a helper
+ # method...
+ proto = SMTPFactory(
+ u'anotheruser@leap.se',
+ self._km, self._config['host'],
+ self._config['port'],
self._config['cert'], self._config['key'],
self._config['encrypted_only']).buildProtocol(('127.0.0.1', 0))
+ # snip...
transport = proto_helpers.StringTransport()
proto.makeConnection(transport)
for i, line in enumerate(self.EMAIL_DATA):
@@ -118,8 +124,10 @@ class TestSmtpGateway(TestCaseWithKeyManager):
"""
Test if message gets encrypted to destination email.
"""
- proto = SMTPFactory(u'anotheruser@leap.se',
- self._km, self._config['host'], self._config['port'],
+ proto = SMTPFactory(
+ u'anotheruser@leap.se',
+ self._km, self._config['host'],
+ self._config['port'],
self._config['cert'], self._config['key'],
self._config['encrypted_only']).buildProtocol(('127.0.0.1', 0))
fromAddr = Address(ADDRESS_2)
@@ -129,7 +137,8 @@ class TestSmtpGateway(TestCaseWithKeyManager):
self._config['port'], self._config['cert'], self._config['key'])
for line in self.EMAIL_DATA[4:12]:
m.lineReceived(line)
- m.eomReceived()
+ #m.eomReceived() # this includes a defer, so we avoid calling it here
+ m.lines.append('') # add a trailing newline
# we need to call the following explicitelly because it was deferred
# inside the previous method
m._maybe_encrypt_and_sign()
@@ -149,7 +158,7 @@ class TestSmtpGateway(TestCaseWithKeyManager):
m._msg.get_payload(1).get_payload(), privkey)
self.assertEqual(
'\n' + '\r\n'.join(self.EMAIL_DATA[9:12]) + '\r\n\r\n--\r\n' +
- 'I prefer encrypted email - https://leap.se/key/anotheruser.\r\n',
+ 'I prefer encrypted email - https://leap.se/key/anotheruser\r\n',
decrypted,
'Decrypted text differs from plaintext.')
@@ -158,8 +167,10 @@ class TestSmtpGateway(TestCaseWithKeyManager):
Test if message gets encrypted to destination email and signed with
sender key.
"""
- proto = SMTPFactory(u'anotheruser@leap.se',
- self._km, self._config['host'], self._config['port'],
+ proto = SMTPFactory(
+ u'anotheruser@leap.se',
+ self._km, self._config['host'],
+ self._config['port'],
self._config['cert'], self._config['key'],
self._config['encrypted_only']).buildProtocol(('127.0.0.1', 0))
user = User(ADDRESS, 'gateway.leap.se', proto, ADDRESS)
@@ -170,7 +181,8 @@ class TestSmtpGateway(TestCaseWithKeyManager):
for line in self.EMAIL_DATA[4:12]:
m.lineReceived(line)
# trigger encryption and signing
- m.eomReceived()
+ #m.eomReceived() # this includes a defer, so we avoid calling it here
+ m.lines.append('') # add a trailing newline
# we need to call the following explicitelly because it was deferred
# inside the previous method
m._maybe_encrypt_and_sign()
@@ -192,7 +204,7 @@ class TestSmtpGateway(TestCaseWithKeyManager):
m._msg.get_payload(1).get_payload(), privkey, verify=pubkey)
self.assertEqual(
'\n' + '\r\n'.join(self.EMAIL_DATA[9:12]) + '\r\n\r\n--\r\n' +
- 'I prefer encrypted email - https://leap.se/key/anotheruser.\r\n',
+ 'I prefer encrypted email - https://leap.se/key/anotheruser\r\n',
decrypted,
'Decrypted text differs from plaintext.')
@@ -202,11 +214,14 @@ class TestSmtpGateway(TestCaseWithKeyManager):
"""
# mock the key fetching
self._km.fetch_keys_from_server = Mock(return_value=[])
- proto = SMTPFactory(u'anotheruser@leap.se',
- self._km, self._config['host'], self._config['port'],
+ proto = SMTPFactory(
+ u'anotheruser@leap.se',
+ self._km, self._config['host'],
+ self._config['port'],
self._config['cert'], self._config['key'],
self._config['encrypted_only']).buildProtocol(('127.0.0.1', 0))
- user = User('ihavenopubkey@nonleap.se', 'gateway.leap.se', proto, ADDRESS)
+ user = User('ihavenopubkey@nonleap.se',
+ 'gateway.leap.se', proto, ADDRESS)
fromAddr = Address(ADDRESS_2)
m = EncryptedMessage(
fromAddr, user, self._km, self._config['host'],
@@ -214,7 +229,8 @@ class TestSmtpGateway(TestCaseWithKeyManager):
for line in self.EMAIL_DATA[4:12]:
m.lineReceived(line)
# trigger signing
- m.eomReceived()
+ #m.eomReceived() # this includes a defer, so we avoid calling it here
+ m.lines.append('') # add a trailing newline
# we need to call the following explicitelly because it was deferred
# inside the previous method
m._maybe_encrypt_and_sign()
@@ -226,8 +242,8 @@ class TestSmtpGateway(TestCaseWithKeyManager):
self.assertEqual('pgp-sha512', m._msg.get_param('micalg'))
# assert content of message
self.assertEqual(
- '\r\n'.join(self.EMAIL_DATA[9:13])+'\r\n--\r\n' +
- 'I prefer encrypted email - https://leap.se/key/anotheruser.\r\n',
+ '\r\n'.join(self.EMAIL_DATA[9:13]) + '\r\n--\r\n' +
+ 'I prefer encrypted email - https://leap.se/key/anotheruser\r\n',
m._msg.get_payload(0).get_payload(decode=True))
# assert content of signature
self.assertTrue(
@@ -262,8 +278,10 @@ class TestSmtpGateway(TestCaseWithKeyManager):
# mock the key fetching
self._km.fetch_keys_from_server = Mock(return_value=[])
# prepare the SMTP factory
- proto = SMTPFactory(u'anotheruser@leap.se',
- self._km, self._config['host'], self._config['port'],
+ proto = SMTPFactory(
+ u'anotheruser@leap.se',
+ self._km, self._config['host'],
+ self._config['port'],
self._config['cert'], self._config['key'],
self._config['encrypted_only']).buildProtocol(('127.0.0.1', 0))
transport = proto_helpers.StringTransport()
@@ -291,8 +309,10 @@ class TestSmtpGateway(TestCaseWithKeyManager):
# mock the key fetching
self._km.fetch_keys_from_server = Mock(return_value=[])
# prepare the SMTP factory with encrypted only equal to false
- proto = SMTPFactory(u'anotheruser@leap.se',
- self._km, self._config['host'], self._config['port'],
+ proto = SMTPFactory(
+ u'anotheruser@leap.se',
+ self._km, self._config['host'],
+ self._config['port'],
self._config['cert'], self._config['key'],
False).buildProtocol(('127.0.0.1', 0))
transport = proto_helpers.StringTransport()