summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpazz <patricktotzke@gmail.com>2011-07-27 23:18:57 +0100
committerpazz <patricktotzke@gmail.com>2011-07-27 23:18:57 +0100
commitf9335d9e1302809ac6ff08266304a5940904c18f (patch)
tree1222cccb6c8ed72a79756788669350e8ccad28c8
parent8c249f650a307097cfb629bd31aefacf40bd00c5 (diff)
reply and friends
-rw-r--r--alot/buffer.py9
-rw-r--r--alot/commandfactory.py7
-rw-r--r--alot/commands.py150
-rw-r--r--alot/helper.py8
-rw-r--r--alot/message.py20
-rw-r--r--alot/settings.py1
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:' +