summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Totzke <patricktotzke@gmail.com>2012-03-11 18:27:30 +0000
committerPatrick Totzke <patricktotzke@gmail.com>2012-03-11 18:27:30 +0000
commitcb46d4fa01109adac8c193e5523928261dc3e284 (patch)
tree194d1844ab64d0c6c88f621f7e409b1146136d6d
parentcb19e5c382c243ad7109fa8ce254331cbc9b3b5f (diff)
refactor: Envelope in its own file
-rw-r--r--alot/commands/envelope.py2
-rw-r--r--alot/commands/globals.py4
-rw-r--r--alot/commands/thread.py2
-rw-r--r--alot/db/envelope.py210
-rw-r--r--alot/db/message.py195
-rw-r--r--docs/source/api/database.rst2
6 files changed, 215 insertions, 200 deletions
diff --git a/alot/commands/envelope.py b/alot/commands/envelope.py
index 8f666b73..21331ca8 100644
--- a/alot/commands/envelope.py
+++ b/alot/commands/envelope.py
@@ -174,7 +174,7 @@ class EditCommand(Command):
def __init__(self, envelope=None, spawn=None, refocus=True, **kwargs):
"""
:param envelope: email to edit
- :type envelope: :class:`~alot.message.Envelope`
+ :type envelope: :class:`~alot.db.envelope.Envelope`
:param spawn: force spawning of editor in a new terminal
:type spawn: bool
:param refocus: m
diff --git a/alot/commands/globals.py b/alot/commands/globals.py
index 77ef05d9..f99aec25 100644
--- a/alot/commands/globals.py
+++ b/alot/commands/globals.py
@@ -20,7 +20,7 @@ from alot import helper
from alot.db.errors import DatabaseLockedError
from alot.completion import ContactsCompleter
from alot.completion import AccountCompleter
-from alot.db.message import Envelope
+from alot.db.envelope import Envelope
from alot import commands
from alot.settings import settings
@@ -458,7 +458,7 @@ class ComposeCommand(Command):
omit_signature=False, spawn=None, **kwargs):
"""
:param envelope: use existing envelope
- :type envelope: :class:`~alot.message.Envelope`
+ :type envelope: :class:`~alot.db.envelope.Envelope`
:param headers: forced header values
:type header: doct (str->str)
:param template: name of template to parse into the envelope after
diff --git a/alot/commands/thread.py b/alot/commands/thread.py
index 50f00d9d..baf0bd1f 100644
--- a/alot/commands/thread.py
+++ b/alot/commands/thread.py
@@ -18,7 +18,7 @@ from alot.db.message import decode_header
from alot.db.message import encode_header
from alot.db.message import extract_headers
from alot.db.message import extract_body
-from alot.db.message import Envelope
+from alot.db.envelope import Envelope
from alot.db.message import Attachment
from alot.db.errors import DatabaseROError
diff --git a/alot/db/envelope.py b/alot/db/envelope.py
new file mode 100644
index 00000000..4af1a05b
--- /dev/null
+++ b/alot/db/envelope.py
@@ -0,0 +1,210 @@
+import os
+import email
+import re
+import email.charset as charset
+charset.add_charset('utf-8', charset.QP, charset.QP, 'utf-8')
+from email.mime.text import MIMEText
+from email.mime.multipart import MIMEMultipart
+
+from alot import __version__
+import logging
+import alot.helper as helper
+from alot.settings import settings
+
+from message import Attachment
+from message 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):
+ """
+ :param template: if not None, the envelope will be initialised by
+ :meth:`parsing <parse_template>` this string before
+ setting any other values given to this constructor.
+ :type template: str
+ :param bodytext: text used as body part
+ :type bodytext: str
+ :param headers: unencoded header values
+ :type headers: dict (str -> unicode)
+ :param attachments: file attachments to include
+ :type attachments: list of :class:`Attachment`
+ """
+ assert isinstance(bodytext, unicode)
+ self.headers = {}
+ self.body = None
+ logging.debug('TEMPLATE: %s' % template)
+ if template:
+ self.parse_template(template)
+ logging.debug('PARSED TEMPLATE: %s' % template)
+ logging.debug('BODY: %s' % self.body)
+ if self.body == None:
+ self.body = bodytext
+ self.headers.update(headers)
+ self.attachments = list(attachments)
+ self.sign = sign
+ self.encrypt = encrypt
+ self.sent_time = None
+ self.modified_since_sent = False
+
+ def __str__(self):
+ return "Envelope (%s)\n%s" % (self.headers, self.body)
+
+ def __setitem__(self, name, val):
+ """setter for header values. this allows adding header like so:
+
+ >>> envelope['Subject'] = u'sm\xf8rebr\xf8d'
+ """
+ self.headers[name] = val
+
+ if self.sent_time:
+ self.modified_since_sent = True
+
+ def __getitem__(self, name):
+ """getter for header values.
+ :raises: KeyError if undefined
+ """
+ return self.headers[name]
+
+ def __delitem__(self, name):
+ del(self.headers[name])
+
+ if self.sent_time:
+ self.modified_since_sent = True
+
+ def __contains__(self, name):
+ return self.headers.__contains__(name)
+
+ def get(self, key, fallback=None):
+ """secure getter for header values that allows specifying a `fallback`
+ return string (defaults to None). This returns the first matching value
+ and doesn't raise KeyErrors"""
+ if key in self.headers:
+ value = self.headers[key][0]
+ else:
+ value = fallback
+ return value
+
+ def get_all(self, key, fallback=[]):
+ """returns all header values for given key"""
+ if key in self.headers:
+ value = self.headers[key]
+ else:
+ value = fallback
+ return value
+
+ def add(self, key, value):
+ """add header value"""
+ if key not in self.headers:
+ self.headers[key] = []
+ self.headers[key].append(value)
+
+ if self.sent_time:
+ self.modified_since_sent = True
+
+ def attach(self, attachment, filename=None, ctype=None):
+ """
+ attach a file
+
+ :param attachment: File to attach, given as :class:`Attachment` object
+ or path to a file.
+ :type attachment: :class:`Attachment` or str
+ :param filename: filename to use in content-disposition.
+ Will be ignored if `path` matches multiple files
+ :param ctype: force content-type to be used for this attachment
+ :type ctype: str
+ """
+
+ if isinstance(attachment, Attachment):
+ self.attachments.append(attachment)
+ elif isinstance(attachment, basestring):
+ path = os.path.expanduser(attachment)
+ part = helper.mimewrap(path, filename, ctype)
+ self.attachments.append(Attachment(part))
+ else:
+ raise TypeError('attach accepts an Attachment or str')
+
+ if self.sent_time:
+ self.modified_since_sent = True
+
+ def construct_mail(self):
+ """
+ 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')
+
+ # wrap it in a multipart container if necessary
+ if self.attachments or self.sign or self.encrypt:
+ msg = MIMEMultipart()
+ msg.attach(textpart)
+ else:
+ msg = textpart
+
+ headers = self.headers.copy()
+ # add Message-ID
+ if 'Message-ID' not in headers:
+ headers['Message-ID'] = [email.Utils.make_msgid()]
+
+ if 'User-Agent' in headers:
+ uastring_format = headers['User-Agent'][0]
+ else:
+ uastring_format = settings.get('user_agent').strip()
+ uastring = uastring_format.format(version=__version__)
+ if uastring:
+ headers['User-Agent'] = [uastring]
+
+ # 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())
+
+ return msg
+
+ def parse_template(self, tmp, reset=False, only_body=False):
+ """parses a template or user edited string to fills this envelope.
+
+ :param tmp: the string to parse.
+ :type tmp: str
+ :param reset: remove previous envelope content
+ :type reset: bool
+ """
+ logging.debug('GoT: """\n%s\n"""' % tmp)
+
+ if self.sent_time:
+ self.modified_since_sent = True
+
+ if only_body:
+ self.body = tmp
+ else:
+ m = re.match('(?P<h>([a-zA-Z0-9_-]+:.+\n)*)\n?(?P<b>(\s*.*)*)',
+ tmp)
+ assert m
+
+ d = m.groupdict()
+ headertext = d['h']
+ self.body = d['b']
+
+ # remove existing content
+ if reset:
+ self.headers = {}
+
+ # go through multiline, utf-8 encoded headers
+ # we decode the edited text ourselves here as
+ # email.message_from_file can't deal with raw utf8 header values
+ key = value = None
+ for line in headertext.splitlines():
+ if re.match('[a-zA-Z0-9_-]+:', line): # new k/v pair
+ if key and value: # save old one from stack
+ self.add(key, value) # save
+ key, value = line.strip().split(':', 1) # parse new pair
+ elif key and value: # append new line without key prefix
+ value += line
+ if key and value: # save last one if present
+ self.add(key, value)
diff --git a/alot/db/message.py b/alot/db/message.py
index aa9b8a1e..08162379 100644
--- a/alot/db/message.py
+++ b/alot/db/message.py
@@ -467,198 +467,3 @@ class Attachment(object):
def get_mime_representation(self):
"""returns mime part that constitutes this attachment"""
return self.part
-
-
-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):
- """
- :param template: if not None, the envelope will be initialised by
- :meth:`parsing <parse_template>` this string before
- setting any other values given to this constructor.
- :type template: str
- :param bodytext: text used as body part
- :type bodytext: str
- :param headers: unencoded header values
- :type headers: dict (str -> unicode)
- :param attachments: file attachments to include
- :type attachments: list of :class:`Attachment`
- """
- assert isinstance(bodytext, unicode)
- self.headers = {}
- self.body = None
- logging.debug('TEMPLATE: %s' % template)
- if template:
- self.parse_template(template)
- logging.debug('PARSED TEMPLATE: %s' % template)
- logging.debug('BODY: %s' % self.body)
- if self.body == None:
- self.body = bodytext
- self.headers.update(headers)
- self.attachments = list(attachments)
- self.sign = sign
- self.encrypt = encrypt
- self.sent_time = None
- self.modified_since_sent = False
-
- def __str__(self):
- return "Envelope (%s)\n%s" % (self.headers, self.body)
-
- def __setitem__(self, name, val):
- """setter for header values. this allows adding header like so:
-
- >>> envelope['Subject'] = u'sm\xf8rebr\xf8d'
- """
- self.headers[name] = val
-
- if self.sent_time:
- self.modified_since_sent = True
-
- def __getitem__(self, name):
- """getter for header values.
- :raises: KeyError if undefined
- """
- return self.headers[name]
-
- def __delitem__(self, name):
- del(self.headers[name])
-
- if self.sent_time:
- self.modified_since_sent = True
-
- def __contains__(self, name):
- return self.headers.__contains__(name)
-
- def get(self, key, fallback=None):
- """secure getter for header values that allows specifying a `fallback`
- return string (defaults to None). This returns the first matching value
- and doesn't raise KeyErrors"""
- if key in self.headers:
- value = self.headers[key][0]
- else:
- value = fallback
- return value
-
- def get_all(self, key, fallback=[]):
- """returns all header values for given key"""
- if key in self.headers:
- value = self.headers[key]
- else:
- value = fallback
- return value
-
- def add(self, key, value):
- """add header value"""
- if key not in self.headers:
- self.headers[key] = []
- self.headers[key].append(value)
-
- if self.sent_time:
- self.modified_since_sent = True
-
- def attach(self, attachment, filename=None, ctype=None):
- """
- attach a file
-
- :param attachment: File to attach, given as :class:`Attachment` object
- or path to a file.
- :type attachment: :class:`Attachment` or str
- :param filename: filename to use in content-disposition.
- Will be ignored if `path` matches multiple files
- :param ctype: force content-type to be used for this attachment
- :type ctype: str
- """
-
- if isinstance(attachment, Attachment):
- self.attachments.append(attachment)
- elif isinstance(attachment, basestring):
- path = os.path.expanduser(attachment)
- part = helper.mimewrap(path, filename, ctype)
- self.attachments.append(Attachment(part))
- else:
- raise TypeError('attach accepts an Attachment or str')
-
- if self.sent_time:
- self.modified_since_sent = True
-
- def construct_mail(self):
- """
- 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')
-
- # wrap it in a multipart container if necessary
- if self.attachments or self.sign or self.encrypt:
- msg = MIMEMultipart()
- msg.attach(textpart)
- else:
- msg = textpart
-
- headers = self.headers.copy()
- # add Message-ID
- if 'Message-ID' not in headers:
- headers['Message-ID'] = [email.Utils.make_msgid()]
-
- if 'User-Agent' in headers:
- uastring_format = headers['User-Agent'][0]
- else:
- uastring_format = settings.get('user_agent').strip()
- uastring = uastring_format.format(version=__version__)
- if uastring:
- headers['User-Agent'] = [uastring]
-
- # 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())
-
- return msg
-
- def parse_template(self, tmp, reset=False, only_body=False):
- """parses a template or user edited string to fills this envelope.
-
- :param tmp: the string to parse.
- :type tmp: str
- :param reset: remove previous envelope content
- :type reset: bool
- """
- logging.debug('GoT: """\n%s\n"""' % tmp)
-
- if self.sent_time:
- self.modified_since_sent = True
-
- if only_body:
- self.body = tmp
- else:
- m = re.match('(?P<h>([a-zA-Z0-9_-]+:.+\n)*)\n?(?P<b>(\s*.*)*)',
- tmp)
- assert m
-
- d = m.groupdict()
- headertext = d['h']
- self.body = d['b']
-
- # remove existing content
- if reset:
- self.headers = {}
-
- # go through multiline, utf-8 encoded headers
- # we decode the edited text ourselves here as
- # email.message_from_file can't deal with raw utf8 header values
- key = value = None
- for line in headertext.splitlines():
- if re.match('[a-zA-Z0-9_-]+:', line): # new k/v pair
- if key and value: # save old one from stack
- self.add(key, value) # save
- key, value = line.strip().split(':', 1) # parse new pair
- elif key and value: # append new line without key prefix
- value += line
- if key and value: # save last one if present
- self.add(key, value)
diff --git a/docs/source/api/database.rst b/docs/source/api/database.rst
index 3f5e7d7c..53fe803b 100644
--- a/docs/source/api/database.rst
+++ b/docs/source/api/database.rst
@@ -55,5 +55,5 @@ Other Structures
.. autoclass:: Attachment
:members:
-.. autoclass:: Envelope
+.. autoclass:: alot.db.envelope.Envelope
:members: