def load(self, session, filename=None): self._mkworkdir(session) self.index = None self.reset(rules=False, data=True) filename = filename or self.conffile lines = [] try: fd = open(filename, 'rb') try: decrypt_and_parse_lines(fd, lambda l: lines.append(l), None) except ValueError: pass fd.close() except IOError: pass # Discover plugins and update the config rule to match from mailpile.plugins import PluginManager self.plugins = PluginManager(config=self, builtin=True).discover([ os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'plugins'), os.path.join(self.workdir, 'plugins') ]) self.sys.plugins.rules['_any'][1] = self.plugins.available() # Parse once (silently), to figure out which plugins to load... self.parse_config(None, '\n'.join(lines), source=filename) if len(self.sys.plugins) == 0: self.sys.plugins.extend(self.plugins.DEFAULT) self.load_plugins(session) # Now all the plugins are loaded, reset and parse again! self.reset_rules_from_source() self.parse_config(session, '\n'.join(lines), source=filename) # Open event log self.event_log = EventLog( self.data_directory('event_log', mode='rw', mkdir=True), # FIXME: Disbled encryption for now lambda: False and self.prefs.obfuscate_index).load() # Enable translations translation = self.get_i18n_translation(session) # Configure jinja2 self.jinja_env = Environment( loader=MailpileJinjaLoader(self), autoescape=True, trim_blocks=True, extensions=[ 'jinja2.ext.i18n', 'jinja2.ext.with_', 'jinja2.ext.do', 'mailpile.jinjaextensions.MailpileCommand' ]) self.jinja_env.install_gettext_translations(translation, newstyle=True) # Load VCards self.vcards = VCardStore( self, self.data_directory('vcards', mode='rw', mkdir=True))
def data_file_and_mimetype(self, ftype, fpath, *args, **kwargs): # The theme gets precedence core_path = self.data_directory(ftype, *args, **kwargs) path, mimetype = os.path.join(core_path, fpath), None # If there's nothing there, check our plugins if not os.path.exists(path): from mailpile.plugins import PluginManager path, mimetype = PluginManager().get_web_asset(fpath, path) if os.path.exists(path): return path, mimetype else: return None, None
from gettext import gettext as _ from mailpile.plugins import PluginManager from mailpile.commands import Command, Action from mailpile.mailutils import Email, ExtractEmails, ExtractEmailAndName from mailpile.vcard import SimpleVCard, VCardLine, AddressInfo from mailpile.util import * _plugins = PluginManager(builtin=__file__) ##[ VCards ]######################################## class VCardCommand(Command): VCARD = "vcard" IS_USER_ACTIVITY = True class CommandResult(Command.CommandResult): IGNORE = ('line_id', 'pid', 'x-rank') def as_text(self): try: return self._as_text() except (KeyError, ValueError, IndexError, TypeError): return '' def _as_text(self): if isinstance(self.result, dict): co = self.command_obj if co.VCARD in self.result:
#coding:utf-8 import os from gettext import gettext as _ from mailpile.plugins import PluginManager from mailpile.crypto.gpgi import GnuPG from mailpile.vcard import * _plugins = PluginManager(builtin=__file__) # User default GnuPG key file DEF_GNUPG_HOME = os.path.expanduser('~/.gnupg') class GnuPGImporter(VCardImporter): FORMAT_NAME = 'GnuPG' FORMAT_DESCRIPTION = _('Import contacts from GnuPG keyring') SHORT_NAME = 'gpg' CONFIG_RULES = { 'active': [_('Enable this importer'), bool, True], 'gpg_home': [_('Location of keyring'), 'path', DEF_GNUPG_HOME], } VCL_KEY_FMT = 'data:application/x-pgp-fingerprint,%s' MERGE_BY = ['key', 'email'] # Merge by Key ID first, email if that fails UPDATE_INDEX = True # Update the index's email->name mapping def get_vcards(self): if not self.config.active:
def _get_ui_elements(self, ui_type, state, context=None): ctx = context or state.get('context_url', '') return copy.deepcopy(PluginManager().get_ui_elements(ui_type, ctx))
import time import copy from pgpdump.utils import PgpdumpException from mailpile.i18n import gettext from mailpile.plugins import PluginManager from mailpile.plugins.keylookup import LookupHandler from mailpile.plugins.keylookup import register_crypto_key_lookup_handler from mailpile.plugins.search import Search from mailpile.mailutils import Email import pgpdump _ = lambda t: t _plugins = PluginManager(builtin=__file__) GLOBAL_KEY_CACHE = {} def _PRUNE_GLOBAL_KEY_CACHE(): global GLOBAL_KEY_CACHE for k in GLOBAL_KEY_CACHE.keys()[10:]: del GLOBAL_KEY_CACHE[k] PGP_KEY_SUFFIXES = ('pub', 'asc', 'key', 'pgp') def _might_be_pgp_key(filename, mimetype): filename = (filename or '').lower()
import mailpile.security as security from mailpile.commands import Command from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.mailutils import Email, FormatMbxId, AddressHeaderParser from mailpile.mailutils import ExtractEmails, ExtractEmailAndName from mailpile.plugins import PluginManager from mailpile.search import MailIndex from mailpile.urlmap import UrlMap from mailpile.util import * from mailpile.ui import SuppressHtmlOutput from mailpile.vfs import vfs, FilePath from mailpile.vcard import AddressInfo _plugins = PluginManager(builtin=__file__) ##[ Shared basic Search Result class]######################################### class SearchResults(dict): _NAME_TITLES = ('the', 'mr', 'ms', 'mrs', 'sir', 'dr', 'lord') def _name(self, sender, short=True, full_email=False): words = re.sub('["<>]', '', sender).split() nomail = [w for w in words if not '@' in w] if nomail: if short: if len(nomail) > 1 and nomail[0].lower() in self._NAME_TITLES: return nomail[1]
"""De-authenticate a user (log out)""" SYNOPSIS = (None, 'logout', 'auth/logout', '[<session ID>]') ORDER = ('Internals', 5) SPLIT_ARG = False IS_INTERACTIVE = True CONFIG_REQUIRED = False HTTP_AUTH_REQUIRED = False HTTP_CALLABLE = ('GET', 'POST') def command(self): # FIXME: Should this only be a POST request? # FIXME: This needs CSRF protection. session_id = self.session.ui.html_variables.get('http_session') if self.args and not session_id: session_id = self.args[0] if session_id: try: self.session.ui.debug('Logging out %s' % session_id) del SESSION_CACHE[session_id] return self._success(_('Goodbye!')) except KeyError: pass return self._error(_('No session found!')) plugin_manager = PluginManager(builtin=True) plugin_manager.register_commands(Authenticate, DeAuthenticate)
import os from gettext import gettext as _ from datetime import date from mailpile.plugins import PluginManager from mailpile.plugins import __all__ as PLUGINS from mailpile.commands import Command from mailpile.crypto.gpgi import GnuPG, SignatureInfo, EncryptionInfo from mailpile.util import * from mailpile.plugins.migrate import Migrate from mailpile.plugins.tags import AddTag, Filter _plugins = PluginManager(builtin=__file__) ##[ Commands ]################################################################ class Setup(Command): """Perform initial setup""" SYNOPSIS = (None, 'setup', None, None) ORDER = ('Internals', 0) LOG_PROGRESS = True TAGS = { 'New': { 'type': 'unread', 'label': False, 'display': 'invisible', 'icon': 'icon-new', 'label_color': '03-gray-dark',
#coding:utf-8 import os import mailpile.security as security from mailpile.commands import Command from mailpile.eventlog import GetThreadEvent from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.crypto.gpgi import GnuPG from mailpile.vcard import * _plugins = PluginManager(builtin=__file__) # User default GnuPG key file DEF_GNUPG_HOME = os.path.expanduser('~/.gnupg') class GnuPGImporter(VCardImporter): FORMAT_NAME = 'GnuPG' FORMAT_DESCRIPTION = _('Import contacts from GnuPG keyring') SHORT_NAME = 'gpg' CONFIG_RULES = { 'active': [_('Enable this importer'), bool, True], 'gpg_home': [_('Location of keyring'), 'path', DEF_GNUPG_HOME], } VCL_KEY_FMT = 'data:application/x-pgp-fingerprint,%s' # Merge by own identifier, email or key (in that order)
from mailpile.crypto.gpgi import GnuPG from mailpile.crypto.gpgi import OpenPGPMimeSigningWrapper from mailpile.crypto.gpgi import OpenPGPMimeEncryptingWrapper from mailpile.crypto.gpgi import OpenPGPMimeSignEncryptWrapper from mailpile.crypto.mime import UnwrapMimeCrypto, MessageAsString from mailpile.crypto.mime import OBSCURE_HEADERS_MILD, OBSCURE_HEADERS_EXTREME from mailpile.crypto.mime import ObscureSubject from mailpile.crypto.state import EncryptionInfo, SignatureInfo from mailpile.eventlog import GetThreadEvent from mailpile.mailutils.addresses import AddressHeaderParser from mailpile.mailutils.emails import Email, MakeContentID, ClearParseCache from mailpile.plugins import PluginManager, EmailTransform from mailpile.plugins.vcard_gnupg import PGPKeysImportAsVCards from mailpile.plugins.search import Search _plugins = PluginManager(builtin=__file__) ##[ GnuPG e-mail processing ]################################################# class ContentTxf(EmailTransform): def _wrap_key_in_html(self, title, keydata): return (( "<html><head><meta charset='utf-8'></head><body>\n" "<h1>%(title)s</h1><p>\n\n%(description)s\n\n</p>" "<pre>\n%(key)s\n</pre><hr>" "<i><a href='%(ad_url)s'>%(ad)s</a>.</i></body></html>" ) % self._wrap_key_in_html_vars(title, keydata)).encode('utf-8') def _wrap_key_in_html_vars(self, title, keydata): return {
import mailbox import os import time from gettext import gettext as _ import mailpile.config from mailpile.plugins import PluginManager from mailpile.util import * from mailpile.commands import Command from mailpile.mailutils import Email _plugins = PluginManager(builtin=os.path.basename(__file__)[:-3]) ##[ Configuration ]########################################################### MAILBOX_FORMATS = ('mbox', 'maildir') _plugins.register_config_variables( 'prefs', { 'export_format': ['Default format for exporting mail', MAILBOX_FORMATS, 'mbox'], }) ##[ Commands ]################################################################ class ExportMail(Command): """Export messages to an external mailbox""" SYNOPSIS = (None, 'export', None, '<msgs> [flat] [<fmt>:<path>]') ORDER = ('Searching', 99)
import time from mailpile.commands import Command from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.util import * _plugins = PluginManager(builtin=__file__) class Events(Command): """Display events from the event log""" SYNOPSIS = (None, 'eventlog', 'eventlog', '[incomplete] [wait] [<count>] [<field>=<val> ...]') ORDER = ('Internals', 9) HTTP_CALLABLE = ('GET', ) HTTP_QUERY_VARS = { 'wait': 'seconds to wait for new data', 'incomplete': 'incomplete events only?', # Filtering by event attributes 'event_id': 'an event ID', 'flag': 'require a flag', 'flags': 'match all flags', 'since': 'wait for new data?', 'source': 'source class', # Filtering by event data (syntax is a bit weird) 'data': 'var:value', 'private_data': 'var:value' }
import datetime import re import time from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.commands import Command from mailpile.plugins.search import Search from mailpile.mailutils import Email _plugins = PluginManager(builtin=__file__) class GPGKeySearch(Command): """Search for a GPG Key.""" ORDER = ('', 0) SYNOPSIS = (None, 'crypto/gpg/searchkey', 'crypto/gpg/searchkey', '<terms>') HTTP_CALLABLE = ('GET', ) HTTP_QUERY_VARS = {'q': 'search terms'} class CommandResult(Command.CommandResult): def as_text(self): if self.result: return '\n'.join([ "%s: %s <%s>" % (keyid, x["name"], x["email"]) for keyid, det in self.result.iteritems() for x in det["uids"] ]) else:
import random import time from mailpile.plugins import PluginManager from mailpile.commands import Command, Action from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.mailutils import Email, ExtractEmails, ExtractEmailAndName from mailpile.mailutils import AddressHeaderParser from mailpile.vcard import VCardLine, VCardStore, MailpileVCard, AddressInfo from mailpile.util import * _plugins = PluginManager(builtin=__file__) ##[ VCards ]######################################## class VCardCommand(Command): VCARD = "vcard" IS_USER_ACTIVITY = True class CommandResult(Command.CommandResult): IGNORE = ('line_id', 'pid', 'x-rank') def as_text(self): try: return self._as_text() except (KeyError, ValueError, IndexError, TypeError): return '' def _as_text(self):
from mailpile.plugins.contacts import ListProfiles from mailpile.plugins.migrate import Migrate from mailpile.plugins.tags import AddTag from mailpile.commands import Command from mailpile.config import SecurePassphraseStorage from mailpile.crypto.gpgi import GnuPG, SignatureInfo, EncryptionInfo from mailpile.crypto.gpgi import GnuPGKeyGenerator, GnuPGKeyEditor from mailpile.httpd import BLOCK_HTTPD_LOCK, Idle_HTTPD from mailpile.smtp_client import SendMail, SendMailError from mailpile.urlmap import UrlMap from mailpile.ui import Session from mailpile.util import * _ = lambda s: s _plugins = PluginManager(builtin=__file__) ##[ Commands ]################################################################ class SetupMagic(Command): """Perform initial setup""" SYNOPSIS = (None, None, None, None) ORDER = ('Internals', 0) LOG_PROGRESS = True TAGS = { 'New': { 'type': 'unread', 'label': False, 'display': 'invisible',
from mailpile.commands import Command from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.plugins.tags import AddTag, DeleteTag, Filter from mailpile.plugins.contacts import * _plugins = PluginManager(builtin=__file__) ##[ Search terms ]############################################################ def search(config, idx, term, hits): group = config._vcards.get(term.split(':', 1)[1]) rt, emails = [], [] if group and group.kind == 'group': for email, attrs in group.get('EMAIL', []): group = config._vcards.get(email.lower(), None) if group: emails.extend([e[0].lower() for e in group.get('EMAIL', [])]) else: emails.append(email.lower()) fromto = term.startswith('group:') and 'from' or 'to' for email in set(emails): rt.extend(hits('{0!s}:{1!s}'.format(email, fromto))) return rt _plugins.register_search_term('group', search) _plugins.register_search_term('togroup', search)
try: from markdown import markdown html = markdown(str(self.result['urlmap'])) except: import traceback print traceback.format_exc() html = '<pre>%s</pre>' % escape_html(self.result['urlmap']) self.result['markdown'] = html return Command.CommandResult.as_html(self, *args, **kwargs) def command(self): prefix = self.args[0] if self.args else None return {'urlmap': UrlMap(self.session).map_as_markdown(prefix=prefix)} plugin_manager = PluginManager(builtin=True) if __name__ != "__main__": plugin_manager.register_commands(HelpUrlMap, UrlRedirect, UrlRedirectEdit, UrlRedirectThread) else: # If run as a python script, print map and run doctests. import doctest import sys import mailpile.app import mailpile.config.defaults as defaults import mailpile.config.manager as cfg_manager import mailpile.plugins import mailpile.ui # Import all the default plugins
import time import mailpile.util import mailpile.security as security from mailpile.commands import Command from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.util import * _plugins = PluginManager(builtin=__file__) class Events(Command): """Display events from the event log""" SYNOPSIS = (None, 'eventlog', 'eventlog', '[incomplete] [wait] [<count>] ' '[<field>=<val> <f>!=<v> <f>=~<re> ...]') ORDER = ('Internals', 9) HTTP_CALLABLE = ('GET', ) HTTP_QUERY_VARS = { 'wait': 'seconds to wait for new data', 'gather': 'gather time (minimum wait), seconds', 'incomplete': 'incomplete events only?', # Filtering by event attributes 'event_id': 'an event ID', 'flag': 'require a flag', 'flags': 'match all flags', 'since': 'wait for new data?', 'source': 'source class', # Filtering by event data (syntax is a bit weird) 'data': 'var:value',
# which should allow us to actually introspect a bit into the behavior # of the classifier. import math import time import datetime from mailpile.commands import Command from mailpile.config.base import ConfigDict from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.mailutils import Email from mailpile.plugins import PluginManager from mailpile.util import * _plugins = PluginManager(builtin=__file__) ##[ Configuration ]########################################################### TAGGERS = {} TRAINERS = {} AUTO_TAG_CONFIG = { 'match_tag': ['Tag we are adding to automatically', str, ''], 'unsure_tag': ['If unsure, add to this tag', str, ''], 'exclude_tags': ['Tags on messages we should never match (ham)', str, []], 'ignore_kws': ['Ignore messages with these keywords', str, []], 'corpus_size': ['How many messages do we train on?', int, 1200], 'threshold': ['Size of the sure/unsure ranges', float, 0.1], 'tagger': ['Internal class name or |shell command', str, ''], 'trainer': ['Internal class name or |shell commant', str, '']
import os from gettext import gettext as _ import mailpile.commands from mailpile.plugins import PluginManager _plugins = PluginManager(builtin=__file__) class Plugins(mailpile.commands.Command): """List the currently available plugins.""" SYNOPSIS = (None, 'plugins', 'plugins', '[<plugins>]') ORDER = ('Config', 9) def command(self): pm = self.session.config.plugins wanted = self.args info = dict((d, { 'loaded': d in pm.LOADED, 'builtin': d not in pm.DISCOVERED }) for d in pm.available() if (not wanted or d in wanted)) for plugin in info: if plugin in pm.DISCOVERED: info[plugin]['manifest'] = pm.DISCOVERED[plugin][1] return self._success(_('Listed available plugins'), info)
from mailpile.crypto.gpgi import GnuPG from mailpile.crypto.gpgi import OpenPGPMimeSigningWrapper from mailpile.crypto.gpgi import OpenPGPMimeEncryptingWrapper from mailpile.crypto.gpgi import OpenPGPMimeSignEncryptWrapper from mailpile.crypto.mime import UnwrapMimeCrypto, MessageAsString from mailpile.crypto.state import EncryptionInfo, SignatureInfo from mailpile.eventlog import GetThreadEvent from mailpile.mailutils.emails import Email, ExtractEmails, ClearParseCache from mailpile.mailutils.emails import MakeContentID from mailpile.plugins import PluginManager, EmailTransform from mailpile.plugins.vcard_gnupg import PGPKeysImportAsVCards from mailpile.plugins.search import Search from mailpile.plugins.keylookup.email_keylookup import get_pgp_key_keywords from mailpile.util import sha1b64 _plugins = PluginManager(builtin=__file__) ##[ Misc. AutoCrypt-related API commands ]#################################### # FIXME: This really should be a record store, not an in-memory dict def save_AutoCrypt_DB(config): if config.autocrypt_db: config.save_pickle(config.autocrypt_db, 'autocrypt_db') def get_AutoCrypt_DB(config): if not config.real_hasattr('autocrypt_db'): try: db = config.load_pickle('autocrypt_db') except (IOError, EOFError):
from gettext import gettext as _ import mailpile.config from mailpile.plugins import PluginManager from mailpile.commands import Command from mailpile.util import * from mailpile.mail_source.mbox import MboxMailSource from mailpile.mail_source.maildir import MaildirMailSource _plugins = PluginManager(builtin=__file__) # We might want to do this differently at some point, but # for now it's fine. def migrate_routes(session): # Migration from route string to messageroute structure def route_parse(route): if route.startswith('|'): command = route[1:].strip() return { "name": command.split()[0], "protocol": "local", "command": command } else: res = re.split( "([\w]+)://([^:]+):([^@]+)@([\w\d.]+):([\d]+)[/]{0,1}", route) if len(res) >= 5: return { "name": _("%(user)s on %(host)s") % {
import mailpile.security as security from mailpile.commands import Command from mailpile.config.defaults import APPVER from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.mail_source.local import LocalMailSource from mailpile.plugins import PluginManager from mailpile.util import * from mailpile.vcard import * _plugins = PluginManager(builtin=__file__) # We might want to do this differently at some point, but # for now it's fine. def migrate_routes(session): # Migration from route string to messageroute structure def route_parse(route): if route.startswith('|'): command = route[1:].strip() return { "name": command.split()[0], "protocol": "local", "command": command } else: res = re.split( "([\w]+)://([^:]+):([^@]+)@([\w\d.]+):([\d]+)[/]{0,1}", route) if len(res) >= 5:
import sys from datetime import datetime as dtime from urllib2 import urlopen from mailpile.commands import Command from mailpile.config.base import PublicConfigRule as p from mailpile.config.defaults import APPVER from mailpile.conn_brokers import Master as ConnBroker from mailpile.plugins import PluginManager from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.util import * _ = lambda t: t _plugins = PluginManager(builtin=__file__) # MARS is the Mailpile Analytics Reporting System. Pretty fancy, huh? # # Details: # https://github.com/mailpile/Mailpile/wiki/Mailpile-Analytics-Reporting-System # MOTD_MARS = '/motd/%(ver)s-%(os)s/motd.json?lang=%(lang)s&py=%(py)s' MOTD_NO_MARS = '/motd/latest/motd.json' # MOTD_URL_DEFAULT = 'https://www.mailpile.is' + MOTD_MARS MOTD_URL_TOR_ONLY = 'http://clgs64523yi2bkhz.onion' + MOTD_MARS MOTD_URL_NO_MARS = 'https://www.mailpile.is' + MOTD_NO_MARS MOTD_URL_TOR_ONLY_NO_MARS = 'http://clgs64523yi2bkhz.onion' + MOTD_NO_MARS MOTD_URLS = {
from mailpile.commands import Command from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.plugins.tags import AddTag, DeleteTag, Filter from mailpile.plugins.contacts import * _plugins = PluginManager(builtin=__file__) ##[ Search terms ]############################################################ def search(config, idx, term, hits): group = config._vcards.get(term.split(':', 1)[1]) rt, emails = [], [] if group and group.kind == 'group': for email, attrs in group.get('EMAIL', []): group = config._vcards.get(email.lower(), None) if group: emails.extend([e[0].lower() for e in group.get('EMAIL', [])]) else: emails.append(email.lower()) fromto = term.startswith('group:') and 'from' or 'to' for email in set(emails): rt.extend(hits('%s:%s' % (email, fromto))) return rt _plugins.register_search_term('group', search) _plugins.register_search_term('togroup', search)
# from urllib2 import urlopen, HTTPError import mailpile.security as security from mailpile.commands import Command from mailpile.conn_brokers import Master as ConnBroker from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.plugins.core import RenderPage from mailpile.ui import SuppressHtmlOutput from mailpile.urlmap import UrlMap from mailpile.util import * _plugins = PluginManager(builtin=__file__) ##[ Commands ]################################################################ class JsApi(RenderPage): """Output API bindings, plugin code and CSS as CSS or Javascript""" SYNOPSIS = (None, None, 'jsapi', None) ORDER = ('Internals', 0) HTTP_CALLABLE = ('GET', ) HTTP_AUTH_REQUIRED = 'Maybe' HTTP_QUERY_VARS = {'ts': 'Cache busting timestamp'} def max_age(self): # Set a long TTL if we know which version of the config this request # applies to, as changed config should avoid the outdated cache.
#coding:utf-8 import os import random import time from gettext import gettext as _ from urllib2 import urlopen import mailpile.util from mailpile.plugins import PluginManager from mailpile.util import * from mailpile.vcard import * _plugins = PluginManager(builtin=__file__) class GravatarImporter(VCardImporter): """ This importer will pull contact details down from a central server, using the Gravatar JSON API and caching thumbnail data locally. For details, see https://secure.gravatar.com/site/implement/ The importer will only pull down a few contacts at a time, to limit the impact on Gravatar's servers and prevent network traffic from stalling the rescan process too much. """ FORMAT_NAME = 'Gravatar' FORMAT_DESCRIPTION = _('Import contact info from a Gravatar server') SHORT_NAME = 'gravatar' CONFIG_RULES = { 'active': [_('Enable this importer'), bool, True],
from datetime import datetime, timedelta import mailpile.security as security from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.vcard import VCardLine, AddressInfo from mailpile.commands import Command from mailpile.mailutils.emails import Email _plugins = PluginManager(builtin=__file__) VCARD_CRYPTO_POLICY = 'X-MAILPILE-CRYPTO-POLICY' CRYPTO_POLICIES = ['none', 'sign', 'encrypt', 'sign-encrypt', 'best-effort', 'default'] ##[ Commands ]################################################################ class CryptoPolicyBaseAction(Command): """ Base class for crypto policy commands """ pass class UpdateCryptoPolicyForUser(CryptoPolicyBaseAction): """ Update crypto policy for a single user """ SYNOPSIS = (None, 'crypto_policy/set', 'crypto_policy/set', '<email address> none|sign|encrypt|sign-encrypt|default') ORDER = ('Internals', 9)
# which should allow us to actually introspect a bit into the behavior # of the classifier. import math import time import datetime import mailpile.config from mailpile.commands import Command from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.mailutils import Email from mailpile.plugins import PluginManager from mailpile.util import * _plugins = PluginManager(builtin=__file__) ##[ Configuration ]########################################################### TAGGERS = {} TRAINERS = {} _plugins.register_config_section('prefs', 'autotag', [ "Auto-tagging", { 'match_tag': ['Tag we are adding to automatically', str, ''], 'unsure_tag': ['If unsure, add to this tag', str, ''], 'exclude_tags': ['Tags on messages we should never match (ham)', str, []], 'ignore_kws': ['Ignore messages with these keywords', str, []], 'corpus_size': ['How many messages do we train on?', int, 1200], 'threshold': ['Size of the sure/unsure ranges', float, 0.1],
if self.data.get('_method', None) == 'POST': password = self.data.get('password', [None])[0] else: password = self.session.ui.get_password( _('Enter your password:'******' ') if policy == 'store': if fingerprint in config.passphrases: del config.passphrases[fingerprint] config.secrets[fingerprint] = { 'password': password, 'policy': policy } return happy(_('Password stored permanently')) elif policy == 'cache-only' and password: sps = SecurePassphraseStorage(password) if ttl > 0: sps.expiration = time.time() + ttl config.passphrases[fingerprint] = sps if fingerprint.lower() in config.secrets: del config.secrets[fingerprint.lower()] return happy(_('Password stored temporarily')) else: return self._error(_('Invalid password policy!'), result) plugin_manager = PluginManager(builtin=True) plugin_manager.register_commands(Authenticate, DeAuthenticate, SetPassphrase)
import math import time import datetime from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager _plugins = PluginManager(builtin=__file__) ##[ Keywords ]################################################################ def meta_kw_extractor(index, msg_mid, msg, msg_size, msg_ts, **kwargs): """Create a search term with the floored log2 size of the message.""" if msg_size <= 0: return [] return ["%s:ln2sz" % int(math.log(msg_size, 2))] _plugins.register_meta_kw_extractor("sizes", meta_kw_extractor) ##[ Search terms ]############################################################ _size_units = {"t": 40, "g": 30, "m": 20, "k": 10, "b": 0} _range_keywords = ["..", "-"]
import mailbox import os import time from gettext import gettext as _ import mailpile.config from mailpile.plugins import PluginManager from mailpile.util import * from mailpile.commands import Command from mailpile.mailutils import Email _plugins = PluginManager(builtin=True) ##[ Configuration ]########################################################### MAILBOX_FORMATS = ('mbox', 'maildir') _plugins.register_config_variables('prefs', { 'export_format': ['Default format for exporting mail', MAILBOX_FORMATS, 'mbox'], }) ##[ Commands ]################################################################ class ExportMail(Command): """Export messages to an external mailbox""" SYNOPSIS = (None, 'export', None, '<msgs> [flat] [<fmt>:<path>]') ORDER = ('Searching', 99)
from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.crypto.state import EncryptionInfo, SignatureInfo _plugins = PluginManager(builtin=__file__) ##[ Keywords ]################################################################ def text_kw_extractor(index, msg, ctype, text): kw = set() if ('-----BEGIN PGP' in text and '\n-----END PGP' in text): kw.add('pgp:has') kw.add('crypto:has') return kw def meta_kw_extractor(index, msg_mid, msg, msg_size, msg_ts): kw, enc, sig = set(), set(), set() def crypto_eval(part): # This is generic if part.encryption_info.get('status') != 'none': enc.add('mp_%s-%s' % ('enc', part.encryption_info['status'])) kw.add('crypto:has') if part.signature_info.get('status') != 'none': sig.add('mp_%s-%s' % ('sig', part.signature_info['status'])) kw.add('crypto:has') if 'cryptostate' in index.config.sys.debug: print 'part status(=%s): enc=%s sig=%s' % (msg_mid,
else: password = self.session.ui.get_password(_('Enter your password:'******' ') if policy == 'store': if fingerprint in config.passphrases: del config.passphrases[fingerprint] config.secrets[fingerprint] = { 'password': password, 'policy': policy } return happy(_('Password stored permanently')) elif policy == 'cache-only' and password: from mailpile.config import SecurePassphraseStorage sps = SecurePassphraseStorage(password) sps.expiration = time.time() + float(ttl) config.passphrases[fingerprint] = sps if fingerprint.lower() in config.secrets: del config.secrets[fingerprint.lower()] return happy(_('Password stored temporarily')) else: return self._error(_('Invalid password policy!'), result) plugin_manager = PluginManager(builtin=True) plugin_manager.register_commands(Authenticate, DeAuthenticate, SetPassphrase)
import datetime import re import time from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.commands import Command from mailpile.plugins.search import Search from mailpile.mailutils import Email _plugins = PluginManager(builtin=__file__) class GPGKeySearch(Command): """Search for a GPG Key.""" ORDER = ('', 0) SYNOPSIS = (None, 'crypto/gpg/searchkey', 'crypto/gpg/searchkey', '<terms>') HTTP_CALLABLE = ('GET', ) HTTP_QUERY_VARS = {'q': 'search terms'} class CommandResult(Command.CommandResult): def as_text(self): if self.result: return '\n'.join(["%s: %s <%s>" % (keyid, x["name"], x["email"]) for keyid, det in self.result.iteritems() for x in det["uids"]]) else: return _("No results") def command(self): args = list(self.args) for q in self.data.get('q', []):
import locale import os import sys import mailpile.util import mailpile.defaults from mailpile.commands import COMMANDS, Command, Action from mailpile.commands import Help, HelpSplash, Load, Rescan from mailpile.config import ConfigManager from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.ui import ANSIColors, Session, UserInteraction, Completer from mailpile.util import * _plugins = PluginManager(builtin=__file__) # This makes sure mailbox "plugins" get loaded... has to go somewhere? from mailpile.mailboxes import * # This is also a bit silly, should be somewhere else? Help.ABOUT = mailpile.defaults.ABOUT # We may try to load readline later on... maybe? readline = None ##[ Main ]#################################################################### def Interact(session):
sock = org_sslwrap(sock, *args, **kwargs) Master.get_fd_context( sock.fileno()).encryption = _explain_encryption(sock) return sock ssl.wrap_socket = SslWrapOnlyOnce if have_ssl_context: # Same again with SSLContext, if we have it. def SslContextWrapOnlyOnce(self, sock, *args, **kwargs): if not isinstance(sock, ssl.SSLSocket): sock = org_context_wrap_socket(self, sock, *args, **kwargs) Master.get_fd_context( sock.fileno()).encryption = _explain_encryption(sock) return sock ssl.SSLContext.wrap_socket = SslContextWrapOnlyOnce from mailpile.plugins import PluginManager _plugins = PluginManager(builtin=__file__) _plugins.register_commands(NetworkHistory) else: import doctest import sys results = doctest.testmod(optionflags=doctest.ELLIPSIS, extraglobs={}) print '%s' % (results, ) if results.failed: sys.exit(1)
from mailpile.i18n import ngettext as _n from mailpile.commands import Command from mailpile.crypto.gpgi import GnuPG from mailpile.crypto.gpgi import OpenPGPMimeSigningWrapper from mailpile.crypto.gpgi import OpenPGPMimeEncryptingWrapper from mailpile.crypto.gpgi import OpenPGPMimeSignEncryptWrapper from mailpile.crypto.mime import UnwrapMimeCrypto, MessageAsString from mailpile.crypto.state import EncryptionInfo, SignatureInfo from mailpile.eventlog import GetThreadEvent from mailpile.mailutils import Email, ExtractEmails, ClearParseCache from mailpile.mailutils import MakeContentID from mailpile.plugins import PluginManager, EmailTransform from mailpile.plugins.vcard_gnupg import PGPKeysImportAsVCards from mailpile.plugins.search import Search _plugins = PluginManager(builtin=__file__) ##[ GnuPG e-mail processing ]################################################# class ContentTxf(EmailTransform): def TransformOutgoing(self, sender, rcpts, msg, **kwargs): matched = False gnupg = None sender_keyid = None # Prefer to just get everything from the profile VCard, in the # common case... profile = self.config.vcards.get_vcard(sender) if profile: sender_keyid = profile.pgp_key
#coding:utf-8 import random import time from urllib2 import urlopen import mailpile.util from mailpile.conn_brokers import Master as ConnBroker from mailpile.i18n import gettext as _ from mailpile.plugins import PluginManager from mailpile.vcard import VCardImporter, MailpileVCard, VCardLine _plugins = PluginManager(builtin=__file__) class LibravatarImporter(VCardImporter): """ This importer will pull contact details down from a central server, using the Libravatar JSON API and caching thumbnail data locally. For details, see https://wiki.libravatar.org/api/ The importer will only pull down a few contacts at a time, to limit the impact on Libravatar's servers and prevent network traffic from stalling the rescan process too much. """ FORMAT_NAME = 'Libravatar' FORMAT_DESCRIPTION = _('Import contact info from a Libravatar server') SHORT_NAME = 'libravatar' CONFIG_RULES = { 'active': [_('Enable this importer'), bool, True],
from mailpile.auth import VerifyAndStorePassphrase from mailpile.config.defaults import APPVER from mailpile.commands import Command from mailpile.crypto.streamer import EncryptingStreamer, DecryptingStreamer from mailpile.plugins import PluginManager from mailpile.plugins.core import Quit from mailpile.i18n import ActivateTranslation from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.ui import SuppressHtmlOutput from mailpile.util import * from mailpile.vfs import FilePath, vfs _ = lambda t: t _plugins = PluginManager(builtin=__file__) def _gzip(filename, data): gzip_data = cStringIO.StringIO() gzip_obj = gzip.GzipFile(filename, 'w', 9, gzip_data, 0) gzip_obj.write(data) gzip_obj.close() return gzip_data.getvalue() def _gunzip(data): with gzip.GzipFile('', 'rb', 0, cStringIO.StringIO(data)) as gzf: return gzf.read()
import socket import threading import time import mailpile.auth import mailpile.util from mailpile.conn_brokers import Master as ConnBroker from mailpile.commands import Command from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.ui import Session from mailpile.util import * _plugins = PluginManager(builtin=__file__) _GUIS = {} def UpdateGUIState(): for gui in _GUIS.values(): gui.change_state() def GetUserSecret(config): """Return a secret that only this Unix user could know.""" return 'FIXME12345' class GuiOMaticConnection(threading.Thread): def __init__(self, config, sock, main=False):
def _get_ui_elements(self, ui_type, state, context=None): self._debug('get_ui_element(%s, %s, ...)' % (ui_type, state)) ctx = context or state.get('context_url', '') return copy.deepcopy(PluginManager().get_ui_elements(ui_type, ctx))
import mailpile.config import mailpile.security as security from mailpile.commands import Command from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.urlmap import UrlMap from mailpile.util import * from mailpile.plugins.search import Search _plugins = PluginManager(builtin=__file__) ##[ Configuration ]########################################################### FILTER_TYPES = ( 'user', # These are the default, user-created filters 'incoming', # These filters are only applied to new messages 'system', # Mailpile core internal filters 'plugin') # Filters created by plugins _plugins.register_config_section( 'tags', [ "Tags", { 'name': ['Tag name', 'str', ''], 'slug': ['URL slug', 'slashslug', ''], # Functional attributes 'type': [
import threading import traceback import mailpile.config import mailpile.security as security from mailpile.commands import Command from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.mailutils import Email from mailpile.plugins import PluginManager from mailpile.smtp_client import sha512_512kCheck, sha512_512kCollide from mailpile.smtp_client import SMTORP_HASHCASH_RCODE, SMTORP_HASHCASH_FORMAT from mailpile.util import * _plugins = PluginManager(builtin=__file__) ##[ Configuration ]########################################################## _plugins.register_config_section( 'sys', 'smtpd', [_('SMTP Daemon'), False, { 'host': (_('Listening host for SMTP daemon'), 'hostname', 'localhost'), 'port': (_('Listening port for SMTP daemon'), int, 0), }]) class SMTPChannel(smtpd.SMTPChannel): MAX_MESSAGE_SIZE = 1024 * 1024 * 50 HASHCASH_WANT_BITS = 8 # Only 128-or-so expensive sha512_512k ops HASHCASH_URL = 'https://www.mailpile.is/hashcash/'
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 _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):
PGPKeysImportAsVCards(self.session, arg=fingerprints).run() # Previous crypto evaluations may now be out of date, so we # clear the cache so users can see results right away. ClearParseCache(pgpmime=True) # i18n note: Not translating things here, since messages are not # generally use-facing and we want to reduce load on our # translators. return self._success('Evaluated key TOFU', result={ 'missing_keys': missing, 'imported_keys': imported, 'status': status, 'on_keychain': old}) PluginManager(builtin=__file__).register_commands( KeyLookup, KeyImport, KeyTofu) ##[ Basic lookup handlers ]################################################### class LookupHandler: NAME = "NONE" TIMEOUT = 2 PRIORITY = 10000 LOCAL = False def __init__(self, session, known_keys_list): self.session = session self.known_keys = known_keys_list def _gnupg(self):
from mailpile.mailutils import ExtractEmails, ExtractEmailAndName, Email from mailpile.mailutils import NotEditableError, AddressHeaderParser from mailpile.mailutils import NoFromAddressError, PrepareMessage from mailpile.mailutils import MakeMessageID from mailpile.search import MailIndex from mailpile.smtp_client import SendMail 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):
import mailpile.util import mailpile.config.defaults from mailpile.commands import COMMANDS, Command, Action from mailpile.config.manager import ConfigManager from mailpile.conn_brokers import DisableUnbrokeredConnections from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.plugins.core import Help, HelpSplash, HealthCheck from mailpile.plugins.core import Load, Rescan, Quit from mailpile.plugins.motd import MessageOfTheDay from mailpile.ui import ANSIColors, Session, UserInteraction, Completer from mailpile.util import * _plugins = PluginManager(builtin=__file__) # This makes sure mailbox "plugins" get loaded... has to go somewhere? from mailpile.mailboxes import * # This is also a bit silly, should be somewhere else? Help.ABOUT = mailpile.config.defaults.ABOUT # We may try to load readline later on... maybe? readline = None ##[ Main ]#################################################################### def CatchUnixSignals(session): def quit_app(sig, stack):
import random import time from gettext import gettext as _ from mailpile.plugins import PluginManager from mailpile.commands import Command, Action from mailpile.mailutils import Email, ExtractEmails, ExtractEmailAndName from mailpile.mailutils import AddressHeaderParser from mailpile.vcard import VCardLine, VCardStore, MailpileVCard, AddressInfo from mailpile.util import * _plugins = PluginManager(builtin=__file__) ##[ VCards ]######################################## class VCardCommand(Command): VCARD = "vcard" IS_USER_ACTIVITY = True class CommandResult(Command.CommandResult): IGNORE = ('line_id', 'pid', 'x-rank') def as_text(self): try: return self._as_text() except (KeyError, ValueError, IndexError, TypeError): return '' def _as_text(self):
import datetime import re import time from gettext import gettext as _ from mailpile.commands import Command, SearchResults from mailpile.mailutils import Email, MBX_ID_LEN from mailpile.mailutils import ExtractEmails, ExtractEmailAndName from mailpile.plugins import PluginManager from mailpile.search import MailIndex from mailpile.urlmap import UrlMap from mailpile.util import * from mailpile.ui import SuppressHtmlOutput _plugins = PluginManager(builtin=__file__) ##[ Commands ]################################################################ class Search(Command): """Search your mail!""" SYNOPSIS = ('s', 'search', 'search', '[@<start>] <terms>') ORDER = ('Searching', 0) HTTP_CALLABLE = ('GET', ) HTTP_QUERY_VARS = { 'q': 'search terms', 'qr': 'search refinements', 'order': 'sort order', 'start': 'start position', 'end': 'end position',
import os import random from gettext import gettext as _ from datetime import date from mailpile.plugins import PluginManager from mailpile.plugins import __all__ as PLUGINS from mailpile.commands import Command from mailpile.crypto.gpgi import GnuPG, SignatureInfo, EncryptionInfo from mailpile.util import * from mailpile.plugins.migrate import Migrate from mailpile.plugins.tags import AddTag _plugins = PluginManager(builtin=__file__) ##[ Commands ]################################################################ class Setup(Command): """Perform initial setup""" SYNOPSIS = (None, 'setup', None, None) ORDER = ('Internals', 0) LOG_PROGRESS = True TAGS = { 'New': { 'type': 'unread', 'label': False, 'display': 'invisible',
from gettext import gettext as _ from mailpile.plugins import PluginManager from mailpile.commands import Command, Action from mailpile.mailutils import Email, ExtractEmails, ExtractEmailAndName from mailpile.vcard import SimpleVCard, VCardLine, AddressInfo from mailpile.util import * _plugins = PluginManager(builtin=__file__) ##[ VCards ]######################################## class VCardCommand(Command): VCARD = "vcard" class CommandResult(Command.CommandResult): IGNORE = ('line_id', 'pid', 'x-rank') def as_text(self): try: return self._as_text() except (KeyError, ValueError, IndexError, TypeError): return '' def _as_text(self): if isinstance(self.result, dict): co = self.command_obj if co.VCARD in self.result: return self._vcards_as_text([self.result[co.VCARD]]) if co.VCARD + 's' in self.result:
#coding:utf-8 import os import mailpile.security as security from mailpile.commands import Command from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.crypto.gpgi import GnuPG from mailpile.vcard import * _plugins = PluginManager(builtin=__file__) # User default GnuPG key file DEF_GNUPG_HOME = os.path.expanduser('~/.gnupg') class GnuPGImporter(VCardImporter): FORMAT_NAME = 'GnuPG' FORMAT_DESCRIPTION = _('Import contacts from GnuPG keyring') SHORT_NAME = 'gpg' CONFIG_RULES = { 'active': [_('Enable this importer'), bool, True], 'gpg_home': [_('Location of keyring'), 'path', DEF_GNUPG_HOME], } VCL_KEY_FMT = 'data:application/x-pgp-fingerprint,%s' # Merge by own identifier, email or key (in that order) MERGE_BY = ['x-gpg-mrgid', 'email']
from gettext import gettext as _ import mailpile.config from mailpile.commands import Command from mailpile.plugins import PluginManager from mailpile.urlmap import UrlMap from mailpile.util import * from mailpile.plugins.search import Search _plugins = PluginManager(builtin=__file__) ##[ Configuration ]########################################################### FILTER_TYPES = ('user', # These are the default, user-created filters 'incoming', # These filters are only applied to new messages 'system', # Mailpile core internal filters 'plugin') # Filters created by plugins _plugins.register_config_section('tags', ["Tags", { 'name': ['Tag name', 'str', ''], 'slug': ['URL slug', 'slashslug', ''], # Functional attributes 'type': ['Tag type', [ 'tag', 'group', 'attribute', 'unread', # Maybe TODO: 'folder', 'shadow', 'drafts', 'blank', 'outbox', 'sent', # composing and sending
# using the appropriate proxying policies. # from urllib2 import urlopen, HTTPError import mailpile.security as security from mailpile.commands import Command from mailpile.conn_brokers import Master as ConnBroker from mailpile.i18n import gettext as _ from mailpile.i18n import ngettext as _n from mailpile.plugins import PluginManager from mailpile.plugins.core import RenderPage from mailpile.ui import SuppressHtmlOutput from mailpile.urlmap import UrlMap from mailpile.util import * _plugins = PluginManager(builtin=__file__) ##[ Commands ]################################################################ class JsApi(RenderPage): """Output API bindings, plugin code and CSS as CSS or Javascript""" SYNOPSIS = (None, None, 'jsapi', None) ORDER = ('Internals', 0) HTTP_CALLABLE = ('GET', ) HTTP_AUTH_REQUIRED = 'Maybe' HTTP_QUERY_VARS = {'ts': 'Cache busting timestamp'} def max_age(self): # Set a long TTL if we know which version of the config this request # applies to, as changed config should avoid the outdated cache.