<commit_before>import datetime
import email.utils
import os
import os.path
import re
import traceback

from mailpile.commands import Command
from mailpile.crypto.state import *
from mailpile.eventlog import Event
from mailpile.i18n import gettext as _
from mailpile.i18n import ngettext as _n
from mailpile.plugins import PluginManager
from mailpile.plugins.tags import Tag
from mailpile.mailutils import ExtractEmails, ExtractEmailAndName, Email
from mailpile.mailutils import NotEditableError, AddressHeaderParser
from mailpile.mailutils import NoFromAddressError, PrepareMessage
from mailpile.smtp_client import SendMail
from mailpile.search import MailIndex
from mailpile.urlmap import UrlMap
from mailpile.util import *
from mailpile.vcard import AddressInfo

from mailpile.plugins.search import Search, SearchResults, View


GLOBAL_EDITING_LOCK = MboxRLock()

_plugins = PluginManager(builtin=__file__)


class EditableSearchResults(SearchResults):
    def __init__(self, session, idx, new, sent, **kwargs):
        SearchResults.__init__(self, session, idx, **kwargs)
        self.new_messages = new
        self.sent_messages = sent
        if new:
            self['created'] = [m.msg_mid() for m in new]
        if sent:
            self['sent'] = [m.msg_mid() for m in new]
            self['summary'] = _('Sent: %s') % self['summary']


def AddComposeMethods(cls):
    class newcls(cls):
        WITH_CONTEXT = (GLOBAL_EDITING_LOCK, )

        def _create_contacts(self, emails):
            try:
                from mailpile.plugins.contacts import AddContact
                AddContact(self.session,
                           arg=['=%s' % e.msg_mid() for e in emails]
                           ).run(recipients=True, quietly=True, internal=True)
            except (TypeError, ValueError, IndexError):
                self._ignore_exception()

        def _tag_emails(self, emails, tag):
            try:
                idx = self._idx()
                idx.add_tag(self.session,
                            self.session.config.get_tag_id(tag),
                            msg_idxs=[e.msg_idx_pos for e in emails],
                            conversation=False)
            except (TypeError, ValueError, IndexError):
                self._ignore_exception()

        def _untag_emails(self, emails, tag):
            try:
                idx = self._idx()
                idx.remove_tag(self.session,
                               self.session.config.get_tag_id(tag),
                               msg_idxs=[e.msg_idx_pos for e in emails],
                               conversation=False)
            except (TypeError, ValueError, IndexError):
                self._ignore_exception()

        def _tagger(self, emails, untag, **kwargs):
            tag = self.session.config.get_tags(**kwargs)
            if tag and untag:
                return self._untag_emails(emails, tag[0]._key)
            elif tag:
                return self._tag_emails(emails, tag[0]._key)

        def _tag_blank(self, emails, untag=False):
            return self._tagger(emails, untag, type='blank')

        def _tag_drafts(self, emails, untag=False):
            return self._tagger(emails, untag, type='drafts')

        def _tag_outbox(self, emails, untag=False):
            return self._tagger(emails, untag, type='outbox')

        def _tag_sent(self, emails, untag=False):
            return self._tagger(emails, untag, type='sent')

        def _track_action(self, action_type, refs):
            session, idx = self.session, self._idx()
            for tag in session.config.get_tags(type=action_type):
                idx.add_tag(session, tag._key,
                            msg_idxs=[m.msg_idx_pos for m in refs])

        def _actualize_ephemeral(self, ephemeral_mid):
            idx = self._idx()

            if isinstance(ephemeral_mid, int):
                # Not actually ephemeral, just return a normal Email
                return Email(idx, ephemeral_mid)

            msgid, mid = ephemeral_mid.rsplit('-', 1)
            etype, etarg, msgid = msgid.split('-', 2)
            if etarg not in ('all', 'att'):
                msgid = etarg + '-' + msgid
            msgid = '<%s>' % msgid.replace('_', '@')
            etype = etype.lower()

            enc_msgid = idx.encode_msg_id(msgid)
            msg_idx = idx.MSGIDS.get(enc_msgid)
            if msg_idx is not None:
                # Already actualized, just return a normal Email
                return Email(idx, msg_idx)

            if etype == 'forward':
                refs = [Email(idx, int(mid, 36))]
                e = Forward.CreateForward(idx, self.session, refs, msgid,
                                          with_atts=(etarg == 'att'))[0]
                self._track_action('fwded', refs)

            elif etype == 'reply':
                refs = [Email(idx, int(mid, 36))]
                e = Reply.CreateReply(idx, self.session, refs, msgid,
                                      reply_all=(etarg == 'all'))[0]
                self._track_action('replied', refs)

            else:
                e = Compose.CreateMessage(idx, self.session, msgid)[0]

            self._tag_blank([e])
            self.session.ui.debug('Actualized: %s' % e.msg_mid())

            return Email(idx, e.msg_idx_pos)

    return newcls


class CompositionCommand(AddComposeMethods(Search)):
    HTTP_QUERY_VARS = {}
    HTTP_POST_VARS = {}
    UPDATE_STRING_DATA = {
        'mid': 'metadata-ID',
        'subject': '..',
        'from': '..',
        'to': '..',
        'cc': '..',
        'bcc': '..',
        'body': '..',
        'encryption': '..',
        'attachment': '..',
    }

    UPDATE_HEADERS = ('Subject', 'From', 'To', 'Cc', 'Bcc', 'Encryption')

    def _new_msgid(self):
        msgid = (email.utils.make_msgid('mailpile')
                 .replace('.', '-')   # Dots may bother JS/CSS
                 .replace('_', '-'))  # We use _ to encode the @ later on
        return msgid

    def _get_email_updates(self, idx, create=False, noneok=False, emails=None):
        # Split the argument list into files and message IDs
        files = [f[1:].strip() for f in self.args if f.startswith('<')]
        args = [a for a in self.args if not a.startswith('<')]

        # Message IDs can come from post data
        for mid in self.data.get('mid', []):
            args.append('=%s' % mid)
        emails = emails or [self._actualize_ephemeral(mid) for mid in
                            self._choose_messages(args, allow_ephemeral=True)]

        update_header_set = (set(self.data.keys()) &<commit_msg>Support attach-pgp-pubkey parameter on message create/update/send/etc.<commit_after>import datetime
import email.utils
import os
import os.path
import re
import traceback

from mailpile.commands import Command
from mailpile.crypto.state import *
from mailpile.eventlog import Event
from mailpile.i18n import gettext as _
from mailpile.i18n import ngettext as _n
from mailpile.plugins import PluginManager
from mailpile.plugins.tags import Tag
from mailpile.mailutils import ExtractEmails, ExtractEmailAndName, Email
from mailpile.mailutils import NotEditableError, AddressHeaderParser
from mailpile.mailutils import NoFromAddressError, PrepareMessage
from mailpile.smtp_client import SendMail
from mailpile.search import MailIndex
from mailpile.urlmap import UrlMap
from mailpile.util import *
from mailpile.vcard import AddressInfo

from mailpile.plugins.search import Search, SearchResults, View


GLOBAL_EDITING_LOCK = MboxRLock()

_plugins = PluginManager(builtin=__file__)


class EditableSearchResults(SearchResults):
    def __init__(self, session, idx, new, sent, **kwargs):
        SearchResults.__init__(self, session, idx, **kwargs)
        self.new_messages = new
        self.sent_messages = sent
        if new:
            self['created'] = [m.msg_mid() for m in new]
        if sent:
            self['sent'] = [m.msg_mid() for m in new]
            self['summary'] = _('Sent: %s') % self['summary']


def AddComposeMethods(cls):
    class newcls(cls):
        WITH_CONTEXT = (GLOBAL_EDITING_LOCK, )

        def _create_contacts(self, emails):
            try:
                from mailpile.plugins.contacts import AddContact
                AddContact(self.session,
                           arg=['=%s' % e.msg_mid() for e in emails]
                           ).run(recipients=True, quietly=True, internal=True)
            except (TypeError, ValueError, IndexError):
                self._ignore_exception()

        def _tag_emails(self, emails, tag):
            try:
                idx = self._idx()
                idx.add_tag(self.session,
                            self.session.config.get_tag_id(tag),
                            msg_idxs=[e.msg_idx_pos for e in emails],
                            conversation=False)
            except (TypeError, ValueError, IndexError):
                self._ignore_exception()

        def _untag_emails(self, emails, tag):
            try:
                idx = self._idx()
                idx.remove_tag(self.session,
                               self.session.config.get_tag_id(tag),
                               msg_idxs=[e.msg_idx_pos for e in emails],
                               conversation=False)
            except (TypeError, ValueError, IndexError):
                self._ignore_exception()

        def _tagger(self, emails, untag, **kwargs):
            tag = self.session.config.get_tags(**kwargs)
            if tag and untag:
                return self._untag_emails(emails, tag[0]._key)
            elif tag:
                return self._tag_emails(emails, tag[0]._key)

        def _tag_blank(self, emails, untag=False):
            return self._tagger(emails, untag, type='blank')

        def _tag_drafts(self, emails, untag=False):
            return self._tagger(emails, untag, type='drafts')

        def _tag_outbox(self, emails, untag=False):
            return self._tagger(emails, untag, type='outbox')

        def _tag_sent(self, emails, untag=False):
            return self._tagger(emails, untag, type='sent')

        def _track_action(self, action_type, refs):
            session, idx = self.session, self._idx()
            for tag in session.config.get_tags(type=action_type):
                idx.add_tag(session, tag._key,
                            msg_idxs=[m.msg_idx_pos for m in refs])

        def _actualize_ephemeral(self, ephemeral_mid):
            idx = self._idx()

            if isinstance(ephemeral_mid, int):
                # Not actually ephemeral, just return a normal Email
                return Email(idx, ephemeral_mid)

            msgid, mid = ephemeral_mid.rsplit('-', 1)
            etype, etarg, msgid = msgid.split('-', 2)
            if etarg not in ('all', 'att'):
                msgid = etarg + '-' + msgid
            msgid = '<%s>' % msgid.replace('_', '@')
            etype = etype.lower()

            enc_msgid = idx.encode_msg_id(msgid)
            msg_idx = idx.MSGIDS.get(enc_msgid)
            if msg_idx is not None:
                # Already actualized, just return a normal Email
                return Email(idx, msg_idx)

            if etype == 'forward':
                refs = [Email(idx, int(mid, 36))]
                e = Forward.CreateForward(idx, self.session, refs, msgid,
                                          with_atts=(etarg == 'att'))[0]
                self._track_action('fwded', refs)

            elif etype == 'reply':
                refs = [Email(idx, int(mid, 36))]
                e = Reply.CreateReply(idx, self.session, refs, msgid,
                                      reply_all=(etarg == 'all'))[0]
                self._track_action('replied', refs)

            else:
                e = Compose.CreateMessage(idx, self.session, msgid)[0]

            self._tag_blank([e])
            self.session.ui.debug('Actualized: %s' % e.msg_mid())

            return Email(idx, e.msg_idx_pos)

    return newcls


class CompositionCommand(AddComposeMethods(Search)):
    HTTP_QUERY_VARS = {}
    HTTP_POST_VARS = {}
    UPDATE_STRING_DATA = {
        'mid': 'metadata-ID',
        'subject': '..',
        'from': '..',
        'to': '..',
        'cc': '..',
        'bcc': '..',
        'body': '..',
        'encryption': '..',
        'attachment': '..',
        'attach-pgp-pubkey': '..',
    }

    UPDATE_HEADERS = ('Subject', 'From', 'To', 'Cc', 'Bcc', 'Encryption', 'Attach-PGP-Pubkey')

    def _new_msgid(self):
        msgid = (email.utils.make_msgid('mailpile')
                 .replace('.', '-')   # Dots may bother JS/CSS
                 .replace('_', '-'))  # We use _ to encode the @ later on
        return msgid

    def _get_email_updates(self, idx, create=False, noneok=False, emails=None):
        # Split the argument list into files and message IDs
        files = [f[1:].strip() for f in self.args if f.startswith('<')]
        args = [a for a in self.args if not a.startswith('<')]

        # Message IDs can come from post data
        for mid in self.data.get('mid', []):
            args.append('=%s' % mid)
        emails = emails or [self._actualize_ephemeral(mid) for mid in
                            self._choose_messages(args, allow_ephemeral=True)]

        update_header_set = (set(self.data.keys()) &
{'fraction_non_alphanumeric': 0.11324289889829874, 'fraction_numerical': 0.002398261260586075, 'mean_word_length': 1.980567344203708, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2615998', 'n_tokens_mistral': 4056, 'n_tokens_neox': 3827, 'n_words': 941}