# Copyright (C) 2011-2018 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import os import urwid from .buffer import Buffer from ..helper import shorten_author_string from ..settings.const import settings from ..widgets.globals import AttachmentWidget def string_sanitize(string, tab_width=8): r""" strips, and replaces non-printable characters :param tab_width: number of spaces to replace tabs with. Read from `globals.tabwidth` setting if `None` :type tab_width: int or `None` >>> string_sanitize(' foo\rbar ', 8) ' foobar ' >>> string_sanitize('foo\tbar', 8) 'foo bar' >>> string_sanitize('foo\t\tbar', 8) 'foo bar' """ string = string.replace('\r', '') lines = list() for line in string.split('\n'): tab_count = line.count('\t') if tab_count > 0: line_length = 0 new_line = list() for i, chunk in enumerate(line.split('\t')): line_length += len(chunk) new_line.append(chunk) if i < tab_count: next_tab_stop_in = tab_width - (line_length % tab_width) new_line.append(' ' * next_tab_stop_in) line_length += next_tab_stop_in lines.append(''.join(new_line)) else: lines.append(line) return '\n'.join(lines) class _HeadersList(urwid.WidgetWrap): """ renders a pile of header values as key/value list """ def __init__(self, headerslist, key_attr, value_attr, gaps_attr=None): """ :param headerslist: list of key/value pairs to display :type headerslist: list of (str, str) :param key_attr: theming attribute to use for keys :type key_attr: urwid.AttrSpec :param value_attr: theming attribute to use for values :type value_attr: urwid.AttrSpec :param gaps_attr: theming attribute to wrap lines in :type gaps_attr: urwid.AttrSpec """ self.headers = headerslist self.key_attr = key_attr self.value_attr = value_attr pile = urwid.Pile(self._build_lines(headerslist)) if gaps_attr is None: gaps_attr = key_attr pile = urwid.AttrMap(pile, gaps_attr) urwid.WidgetWrap.__init__(self, pile) def __str__(self): return str(self.headers) def _build_lines(self, lines): max_key_len = 1 headerlines = [] # calc max length of key-string for key, value in lines: if len(key) > max_key_len: max_key_len = len(key) for key, value in lines: # todo : even/odd keyw = ('fixed', max_key_len + 1, urwid.Text((self.key_attr, key))) valuew = urwid.Text((self.value_attr, value)) line = urwid.Columns([keyw, valuew]) headerlines.append(line) return headerlines class EnvelopeBuffer(Buffer): """message composition mode""" modename = 'envelope' def __init__(self, envelope): self.envelope = envelope self.all_headers = False self.rebuild() super().__init__() def __str__(self): to = self.envelope.get('To', fallback='unset') return '[envelope] to: %s' % (shorten_author_string(to, 400)) async def get_info(self): info = {} info['to'] = self.envelope.get('To', fallback='unset') return info def cleanup(self): if self.envelope.tmpfile: os.unlink(self.envelope.tmpfile.name) def rebuild(self): displayed_widgets = [] hidden = settings.get('envelope_headers_blacklist') # build lines lines = [] for (k, vlist) in self.envelope.headers.items(): if (k not in hidden) or self.all_headers: for value in vlist: lines.append((k, value)) # sign/encrypt lines if self.envelope.sign: description = 'Yes' sign_key = self.envelope.sign_key if sign_key is not None and len(sign_key.subkeys) > 0: description += ', with key ' + sign_key.uids[0].uid lines.append(('GPG sign', description)) if self.envelope.encrypt: description = 'Yes' encrypt_keys = self.envelope.encrypt_keys.values() if len(encrypt_keys) == 1: description += ', with key ' elif len(encrypt_keys) > 1: description += ', with keys ' key_ids = [] for key in encrypt_keys: if key is not None and key.subkeys: key_ids.append(key.uids[0].uid) description += ', '.join(key_ids) lines.append(('GPG encrypt', description)) if self.envelope.tags: lines.append(('Tags', ','.join(self.envelope.tags))) # add header list widget iff header values exists if lines: key_att = settings.get_theming_attribute('envelope', 'header_key') value_att = settings.get_theming_attribute('envelope', 'header_value') gaps_att = settings.get_theming_attribute('envelope', 'header') self.header_wgt = _HeadersList(lines, key_att, value_att, gaps_att) displayed_widgets.append(self.header_wgt) # display attachments lines = [] for a in self.envelope.attachments: lines.append(AttachmentWidget(a, selectable=False)) if lines: self.attachment_wgt = urwid.Pile(lines) displayed_widgets.append(self.attachment_wgt) self.body_wgt = urwid.Text(string_sanitize(self.envelope.body)) displayed_widgets.append(self.body_wgt) self.body = urwid.ListBox(displayed_widgets) def toggle_all_headers(self): """toggles visibility of all envelope headers""" self.all_headers = not self.all_headers self.rebuild()