summaryrefslogtreecommitdiff
path: root/alot/db/envelope.py
diff options
context:
space:
mode:
authorPatrick Totzke <patricktotzke@gmail.com>2012-05-17 15:47:23 +0100
committerPatrick Totzke <patricktotzke@gmail.com>2012-05-17 15:47:23 +0100
commit08c2ee1d88e69aa5df27b74d1cebd3c0c8be68f6 (patch)
tree2dde7d7db9146918242f2c289c64e587706b3f09 /alot/db/envelope.py
parent138f13c682f6a63da67cced1f0c267b5f6f349b8 (diff)
parent56b0b8a0f310403d4664649ab9b69d3cd765dbe5 (diff)
Merge branch '0.3-feature-pyme' into staging
Conflicts: alot/settings/__init__.py alot/settings/checks.py docs/source/generate_configs.py
Diffstat (limited to 'alot/db/envelope.py')
-rw-r--r--alot/db/envelope.py81
1 files changed, 68 insertions, 13 deletions
diff --git a/alot/db/envelope.py b/alot/db/envelope.py
index f84d305a..14859d9f 100644
--- a/alot/db/envelope.py
+++ b/alot/db/envelope.py
@@ -1,15 +1,23 @@
+# vim:ts=4:sw=4:expandtab
import os
import email
import re
import email.charset as charset
charset.add_charset('utf-8', charset.QP, charset.QP, 'utf-8')
+from email.encoders import encode_7or8bit
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
+from email.mime.application import MIMEApplication
+import pyme.core
+import pyme.constants
+import pyme.errors
from alot import __version__
import logging
import alot.helper as helper
+import alot.crypto as crypto
from alot.settings import settings
+from alot.errors import GPGProblem
from attachment import Attachment
from utils import encode_header
@@ -18,7 +26,7 @@ from utils import encode_header
class Envelope(object):
"""a message that is not yet sent and still editable"""
def __init__(self, template=None, bodytext=u'', headers={}, attachments=[],
- sign=False, encrypt=False):
+ sign=False, sign_key=None, encrypt=False):
"""
:param template: if not None, the envelope will be initialised by
:meth:`parsing <parse_template>` this string before
@@ -44,6 +52,7 @@ class Envelope(object):
self.headers.update(headers)
self.attachments = list(attachments)
self.sign = sign
+ self.sign_key = sign_key
self.encrypt = encrypt
self.sent_time = None
self.modified_since_sent = False
@@ -133,15 +142,65 @@ class Envelope(object):
compiles the information contained in this envelope into a
:class:`email.Message`.
"""
- # build body text part
- textpart = MIMEText(self.body.encode('utf-8'), 'plain', 'utf-8')
+ # Build body text part. To properly sign/encrypt messages later on, we
+ # convert the text to its canonical format (as per RFC 2015).
+ canonical_format = self.body.encode('utf-8')
+ canonical_format = canonical_format.replace('\\t', ' '*4)
+ textpart = MIMEText(canonical_format, 'plain', 'utf-8')
# wrap it in a multipart container if necessary
- if self.attachments or self.sign or self.encrypt:
- msg = MIMEMultipart()
- msg.attach(textpart)
+ if self.attachments:
+ inner_msg = MIMEMultipart()
+ inner_msg.attach(textpart)
+ # add attachments
+ for a in self.attachments:
+ inner_msg.attach(a.get_mime_representation())
else:
- msg = textpart
+ inner_msg = textpart
+
+ if self.sign:
+ context = crypto.CryptoContext()
+
+ plaintext = crypto.email_as_string(inner_msg)
+ logging.info('signing plaintext: ' + plaintext)
+
+ try:
+ result, signature_str = context.detached_signature_for(
+ plaintext, self.sign_key)
+ if len(result.signatures) != 1:
+ raise GPGProblem(("Could not sign message "
+ "(GPGME did not return a signature)"))
+ except pyme.errors.GPGMEError as e:
+ # 11 == GPG_ERR_BAD_PASSPHRASE
+ if e.getcode() == 11:
+ # If GPG_AGENT_INFO is unset or empty, the user just does
+ # not have gpg-agent running (properly).
+ if os.environ.get('GPG_AGENT_INFO', '').strip() == '':
+ raise GPGProblem(("Bad passphrase and "
+ "GPG_AGENT_INFO not set. Please setup "
+ "gpg-agent."))
+ else:
+ raise GPGProblem(("Bad passphrase. Is "
+ "gpg-agent running?"))
+ raise GPGProblem(str(e))
+
+ micalg = crypto.RFC3156_micalg_from_result(result)
+ outer_msg = MIMEMultipart('signed', micalg=micalg,
+ protocol='application/pgp-signature')
+
+ # wrap signature in MIMEcontainter
+ signature_mime = MIMEApplication(_data=signature_str,
+ _subtype='pgp-signature; name="signature.asc"',
+ _encoder=encode_7or8bit)
+ signature_mime['Content-Description'] = 'signature'
+ signature_mime.set_charset('us-ascii')
+
+ # add signed message and signature to outer message
+ outer_msg.attach(inner_msg)
+ outer_msg.attach(signature_mime)
+ outer_msg['Content-Disposition'] = 'inline'
+ else:
+ outer_msg = inner_msg
headers = self.headers.copy()
# add Message-ID
@@ -159,13 +218,9 @@ class Envelope(object):
# copy headers from envelope to mail
for k, vlist in headers.items():
for v in vlist:
- msg[k] = encode_header(k, v)
-
- # add attachments
- for a in self.attachments:
- msg.attach(a.get_mime_representation())
+ outer_msg[k] = encode_header(k, v)
- return msg
+ return outer_msg
def parse_template(self, tmp, reset=False, only_body=False):
"""parses a template or user edited string to fills this envelope.