diff options
author | pazz <patricktotzke@gmail.com> | 2011-07-27 23:18:57 +0100 |
---|---|---|
committer | pazz <patricktotzke@gmail.com> | 2011-07-27 23:18:57 +0100 |
commit | f9335d9e1302809ac6ff08266304a5940904c18f (patch) | |
tree | 1222cccb6c8ed72a79756788669350e8ccad28c8 | |
parent | 8c249f650a307097cfb629bd31aefacf40bd00c5 (diff) |
reply and friends
-rw-r--r-- | alot/buffer.py | 9 | ||||
-rw-r--r-- | alot/commandfactory.py | 7 | ||||
-rw-r--r-- | alot/commands.py | 150 | ||||
-rw-r--r-- | alot/helper.py | 8 | ||||
-rw-r--r-- | alot/message.py | 20 | ||||
-rw-r--r-- | alot/settings.py | 1 |
6 files changed, 132 insertions, 63 deletions
diff --git a/alot/buffer.py b/alot/buffer.py index 7b8285d5..06c2986f 100644 --- a/alot/buffer.py +++ b/alot/buffer.py @@ -19,8 +19,9 @@ Copyright (C) 2011 Patrick Totzke <patricktotzke@gmail.com> import urwid import widgets +from settings import config from walker import IteratorWalker -from itertools import izip_longest +from message import decode_header class Buffer: @@ -235,7 +236,7 @@ class EnvelopeBuffer(Buffer): self.autoparms = {'email': self.get_email} def __str__(self): - return "to: %s" % self.email['To'] + return "to: %s" % decode_header(self.email['To']) def get_email(self): return self.email @@ -246,7 +247,9 @@ class EnvelopeBuffer(Buffer): def rebuild(self): displayed_widgets = [] - self.header_wgt = widgets.MessageHeaderWidget(self.email) + dh = config.getstringlist('general', 'displayed_headers') + self.header_wgt = widgets.MessageHeaderWidget(self.email, + displayed_headers=dh) displayed_widgets.append(self.header_wgt) self.body_wgt = widgets.MessageBodyWidget(self.email) displayed_widgets.append(self.body_wgt) diff --git a/alot/commandfactory.py b/alot/commandfactory.py index 0b653068..e12eb7ec 100644 --- a/alot/commandfactory.py +++ b/alot/commandfactory.py @@ -47,7 +47,7 @@ COMMANDS = { 'toggletag': (commands.ToggleThreadTagCommand, {'tag': 'inbox'}), # envelope 'send': (commands.SendMailCommand, {}), - 'reedit': (commands.EnvelopeReeditCommand, {}), + 'reedit': (commands.EnvelopeEditCommand, {}), 'subject': (commands.EnvelopeSetCommand, {'key': 'Subject'}), 'to': (commands.EnvelopeSetCommand, {'key': 'To'}), @@ -56,6 +56,7 @@ COMMANDS = { 'retagprompt': (commands.RetagPromptCommand, {}), # thread 'reply': (commands.ReplyCommand, {}), + 'bounce': (commands.BounceMailCommand, {}), } @@ -110,7 +111,7 @@ ALLOWED_COMMANDS = { 'envelope': ['send', 'reedit', 'to', 'subject'] + globalcomands, 'bufferlist': ['openfocussed', 'closefocussed'] + globalcomands, 'taglist': globalcomands, - 'thread': ['toggletag', 'reply'] + globalcomands, + 'thread': ['toggletag', 'reply', 'bounce'] + globalcomands, } @@ -143,7 +144,7 @@ def interpret_commandline(cmdline, mode): if cmd in ['exit', 'flush', 'pyshell', 'taglist', 'close', 'compose', 'openfocussed', 'closefocussed', 'bnext', 'bprevious', 'retag', 'refresh', 'bufferlist', 'refineprompt', 'reply', - 'openthread', 'send', 'reedit', 'retagprompt']: + 'bounce', 'openthread', 'send', 'reedit', 'retagprompt']: return commandfactory(cmd) else: return None diff --git a/alot/commands.py b/alot/commands.py index aeee2f33..a643b827 100644 --- a/alot/commands.py +++ b/alot/commands.py @@ -26,6 +26,9 @@ import StringIO import email from email.parser import Parser from email.mime.text import MIMEText +from email.mime.multipart import MIMEMultipart +from email import Charset +from email.header import Header import tempfile import buffer @@ -35,6 +38,8 @@ from db import DatabaseLockedError from completion import ContactsCompleter from completion import AccountCompleter import helper +from message import decode_to_unicode +from message import decode_header class Command: @@ -366,13 +371,12 @@ class SendMailCommand(Command): class ComposeCommand(Command): - def __init__(self, mail=None, headers=None, **kwargs): - if headers: - self.headers = headers + def __init__(self, mail=None, **kwargs): + if not mail: + self.mail = MIMEMultipart() + self.mail.attach(MIMEText('', 'plain', 'UTF-8')) else: - self.headers = {} - self.mail = mail - logging.debug(headers) + self.mail = mail Command.__init__(self, **kwargs) def apply(self, ui): @@ -395,37 +399,17 @@ class ComposeCommand(Command): ui.notify('canceled') return a = ui.accountman.get_account_by_address(fromaddress) - self.headers['From'] = "%s <%s>" % (a.realname, a.address) + self.mail['From'] = "%s <%s>" % (a.realname, a.address) #get To header - if 'To' not in self.headers: - self.headers['To'] = ui.prompt(prefix='To>', - completer=ContactsCompleter()) + if 'To' not in self.mail: + self.mail['To'] = ui.prompt(prefix='To>', + completer=ContactsCompleter()) if settings.config.getboolean('general', 'ask_subject') and \ - not 'Subject' in self.headers: - self.headers['Subject'] = ui.prompt(prefix='Subject>') + not 'Subject' in self.mail: + self.mail['Subject'] = ui.prompt(prefix='Subject>') - def openEnvelopeFromTmpfile(): - f = open(tf.name) - editor_input = f.read() - self.email = Parser().parsestr(editor_input) - f.close() - os.unlink(tf.name) - ui.apply_command(OpenEnvelopeCommand(email=self.email)) - - tf = tempfile.NamedTemporaryFile(delete=False) - if self.mail: - for k, v in self.headers: - self.mail[k] = v - tf.write(self.mail.as_string()) - else: - for i in self.headers.items(): - tf.write('%s: %s\n' % i) - tf.write('\n\n') - tf.close() - ui.apply_command(EditCommand(tf.name, - on_success=openEnvelopeFromTmpfile, - refocus=False)) + ui.apply_command(OpenEnvelopeCommand(email=self.mail)) class ReplyCommand(Command): @@ -437,33 +421,55 @@ class ReplyCommand(Command): def apply(self, ui): msg = ui.current_buffer.get_selected_message() mail = msg.get_email() - mailcontent = '' + # set body text + mailcontent = '\nOn %s, %s wrote:\n' % (msg.get_datestring(), + msg.get_author()[0]) + for line in msg.accumulate_body().splitlines(): + mailcontent += '>' + line + '\n' + + Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8') + bodypart = MIMEText(mailcontent.encode('utf-8'), 'plain', 'UTF-8') + reply = MIMEMultipart() + reply.attach(bodypart) + + # copy subject + subject = mail['Subject'] + if not subject.startswith('Re:'): + subject = 'Re: ' + subject + reply['Subject'] = Header(subject.encode('utf-8'), 'UTF-8').encode() + + # set to + reply['To'] = Header(mail['From'].encode('utf-8'), 'UTF-8').encode() + # set In-Reply-To header - mailcontent += 'In-Reply-To: %s' % msg.get_message_id() + del(reply['In-Reply-To']) + reply['In-Reply-To'] = msg.get_message_id() # set References header old_references = mail['References'] if old_references: old_references = old_references.split() references = old_references[-8:] - if len(old_references)>8: + if len(old_references) > 8: references = old_references[:1] + references references.append(msg.get_message_id()) - mailcontent += 'References: %s' % ' '.join(references) + reply['References'] = ' '.join(references) + #TODO # extract from address from to,cc,bcc fields or leave blank # (composeCommand will prompt) - # set body text - mailcontent += '\nOn %s, %s wrote' % (msg.get_datestring(), - msg.get_author()[0]) - for line in msg.accumulate_body().split(): - mailcontent += '>' + line + '\n' - - reply = email.message_from_string(mailcontent) ui.apply_command(ComposeCommand(mail=reply)) +class BounceMailCommand(Command): + def apply(self, ui): + msg = ui.current_buffer.get_selected_message() + mail = msg.get_email() + del(mail['To']) + ui.apply_command(ComposeCommand(mail=mail)) + + class RetagPromptCommand(Command): """start a commandprompt to retag selected threads' tags""" @@ -526,25 +532,63 @@ class RefinePromptCommand(Command): ui.commandprompt('refine ' + oldquery) -class EnvelopeReeditCommand(Command): +class EnvelopeEditCommand(Command): """re-edits mail in from envelope buffer""" def apply(self, ui): - in_mail = ui.current_buffer.get_email() + self.mail = ui.current_buffer.get_email() - def readTmpfile(): + def openEnvelopeFromTmpfile(): f = open(tf.name) - editor_input = f.read() - out_mail = Parser().parsestr(editor_input) + editor_input = f.read().decode('utf-8') + + #split editor out + headertext, bodytext = editor_input.split('\n\n', 1) + + #reply = MIMEText(mailcontent.encode('utf-8'), 'plain', 'UTF-8') + for line in headertext.splitlines(): + logging.debug(line) + key, value = line.split(':', 1) + value = value.strip().encode('utf-8') + del self.mail[key] # ensure there is only one + self.mail[key] = Header(value, 'UTF-8').encode() + + Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8') + if self.mail.is_multipart(): + for part in self.mail.walk(): + if part.get_content_maintype() == 'text': + part.set_payload(bodytext, 'utf-8') + break + f.close() os.unlink(tf.name) - # TODO: attachments? - ui.current_buffer.set_email(out_mail) + ui.current_buffer.set_email(self.mail) + + # decode header + edit_headers = ['Subject', 'To', 'From'] + headertext = u'' + for key in edit_headers: + value = u'' + if key in self.mail: + value = decode_header(self.mail[key]) + headertext += '%s: %s\n' % (key, value) + + if self.mail.is_multipart(): + for part in self.mail.walk(): + if part.get_content_maintype() == 'text': + bodytext = decode_to_unicode(part) + break + else: + bodytext = decode_to_unicode(self.mail) + #write stuff to tempfile tf = tempfile.NamedTemporaryFile(delete=False) - tf.write(in_mail.as_string()) + content = '%s\n\n%s' % (headertext, + bodytext) + tf.write(content.encode('utf-8')) + tf.flush() tf.close() ui.apply_command(EditCommand(tf.name, - on_success=readTmpfile, + on_success=openEnvelopeFromTmpfile, refocus=False)) diff --git a/alot/helper.py b/alot/helper.py index 5c547436..92ed76e9 100644 --- a/alot/helper.py +++ b/alot/helper.py @@ -33,13 +33,13 @@ def shorten(string, maxlen): def pretty_datetime(d): today = date.today() if today == d.date(): - string = unicode(d.strftime('%H:%M%P')) + string = d.strftime('%H:%M%P') elif d.date() == today - timedelta(1): - string = unicode('Yest.%2d' % d.hour + d.strftime('%P')) + string = 'Yest.%2d' % d.hour + d.strftime('%P') elif d.year != today.year: - string = unicode(d.strftime('%b %Y')) + string = d.strftime('%b %Y') else: - string = unicode(d.strftime('%b %d')) + string = d.strftime('%b %d') return string diff --git a/alot/message.py b/alot/message.py index c165ab5e..198980cf 100644 --- a/alot/message.py +++ b/alot/message.py @@ -189,6 +189,26 @@ def extract_body(mail): # else drop return bodytxt + +def decode_to_unicode(part): + enc = part.get_content_charset() + raw_payload = part.get_payload(decode=True) + if enc: + raw_payload = unicode(raw_payload, enc) + else: + raw_payload = unicode(raw_payload, errors='replace') + return raw_payload + +def decode_header(header): + valuelist = email.header.decode_header(header) + value = u'' + for v, enc in valuelist: + if enc: + value = value + v.decode(enc) + else: + value = value + v + return value + class Attachment: """represents a single mail attachment""" diff --git a/alot/settings.py b/alot/settings.py index 5b288069..69f1c7a7 100644 --- a/alot/settings.py +++ b/alot/settings.py @@ -315,6 +315,7 @@ MAPPING = { }, 'thread': { 'a': ('toggletag inbox', ''), + 'r': ('reply', ''), }, 'taglist': { # 'enter': ('search', {'query': (lambda: 'tag:' + |