Full Code of mailpile/Mailpile for AI

master 741e610f3e76 cached
624 files
5.2 MB
1.4M tokens
4413 symbols
1 requests
Download .txt
Showing preview only (5,534K chars total). Download the full file or copy to clipboard to get everything.
Repository: mailpile/Mailpile
Branch: master
Commit: 741e610f3e76
Files: 624
Total size: 5.2 MB

Directory structure:
gitextract_dkjlc6xq/

├── .coveragerc
├── .dockerignore
├── .gitignore
├── .gitmodules
├── .travis.yml
├── .tx/
│   └── config
├── AGPLv3.txt
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── COPYING.md
├── DEV_FAQ.md
├── Dockerfile
├── Dockerfile.dev
├── Gruntfile.js
├── MANIFEST.in
├── Makefile
├── README.md
├── babel.cfg
├── bower.json
├── docker-compose.dev.yml
├── docker-compose.yml
├── install_hooks.py
├── mailpile/
│   ├── __init__.py
│   ├── __main__.py
│   ├── app.py
│   ├── auth.py
│   ├── command_cache.py
│   ├── commands.py
│   ├── config/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── defaults.py
│   │   ├── detect.py
│   │   ├── manager.py
│   │   ├── paths.py
│   │   └── validators.py
│   ├── conn_brokers.py
│   ├── crypto/
│   │   ├── __init__.py
│   │   ├── aes_utils.py
│   │   ├── autocrypt.py
│   │   ├── gpgi.py
│   │   ├── keyinfo.py
│   │   ├── mime.py
│   │   ├── records.py
│   │   ├── state.py
│   │   ├── streamer.py
│   │   └── tor.py
│   ├── eventlog.py
│   ├── httpd.py
│   ├── i18n.py
│   ├── index/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── mailboxes.py
│   │   ├── msginfo.py
│   │   └── search.py
│   ├── mail_source/
│   │   ├── __init__.py
│   │   ├── imap.py
│   │   ├── imap_starttls.py
│   │   ├── imap_utf7.py
│   │   ├── local.py
│   │   └── pop3.py
│   ├── mailboxes/
│   │   ├── __init__.py
│   │   ├── gmvault.py
│   │   ├── macmail.py
│   │   ├── maildir.py
│   │   ├── maildirwin.py
│   │   ├── mbox.py
│   │   ├── pop3.py
│   │   └── wervd.py
│   ├── mailutils/
│   │   ├── __init__.py
│   │   ├── addresses.py
│   │   ├── emails.py
│   │   ├── generator.py
│   │   ├── header.py
│   │   ├── headerprint.py
│   │   ├── html.py
│   │   ├── safe.py
│   │   └── vcal.py
│   ├── packing.py
│   ├── platforms.py
│   ├── plugins/
│   │   ├── __init__.py
│   │   ├── autotag.py
│   │   ├── autotag_sb.py
│   │   ├── backups.py
│   │   ├── compose.py
│   │   ├── contacts.py
│   │   ├── core.py
│   │   ├── crypto_autocrypt.py
│   │   ├── crypto_gnupg.py
│   │   ├── crypto_policy.py
│   │   ├── cryptostate.py
│   │   ├── dates.py
│   │   ├── eventlog.py
│   │   ├── exporters.py
│   │   ├── groups.py
│   │   ├── gui.py
│   │   ├── html_magic.py
│   │   ├── keylookup/
│   │   │   ├── __init__.py
│   │   │   ├── email_keylookup.py
│   │   │   └── wkd.py
│   │   ├── migrate.py
│   │   ├── motd.py
│   │   ├── oauth.py
│   │   ├── plugins.py
│   │   ├── search.py
│   │   ├── setup_magic.py
│   │   ├── setup_magic_ispdb.py
│   │   ├── sizes.py
│   │   ├── smtp_server.py
│   │   ├── tags.py
│   │   ├── vcard_carddav.py
│   │   ├── vcard_gnupg.py
│   │   ├── vcard_gravatar.py
│   │   ├── vcard_libravatar.py
│   │   ├── vcard_mork.py
│   │   └── webterminal.py
│   ├── postinglist.py
│   ├── safe_popen.py
│   ├── search.py
│   ├── search_history.py
│   ├── security.py
│   ├── smtp_client.py
│   ├── spambayes/
│   │   ├── LICENSE.txt
│   │   ├── Options.py
│   │   ├── OptionsClass.py
│   │   ├── Tester.py
│   │   ├── __init__.py
│   │   ├── chi2.py
│   │   ├── classifier.py
│   │   └── safepickle.py
│   ├── tests/
│   │   ├── TESTS
│   │   ├── __init__.py
│   │   ├── data/
│   │   │   ├── Maildir/
│   │   │   │   ├── cur/
│   │   │   │   │   ├── .gitkeep
│   │   │   │   │   ├── 1379857166.25979_1.hottie,2,S
│   │   │   │   │   ├── 1379857166.25979_3.hottie,2,S
│   │   │   │   │   ├── 1379857166.25979_5.hottie,2,S
│   │   │   │   │   ├── 1379857166.25979_7.hottie,2,S
│   │   │   │   │   ├── 1379857166.25979_9.hottie,2,S
│   │   │   │   │   ├── 1379857166.25980_9.hottie,2,S
│   │   │   │   │   ├── 1379857166.25981_10.hottie,2,S
│   │   │   │   │   ├── broken_email_1396694612
│   │   │   │   │   ├── encrypted-empty-content-w-key.eml
│   │   │   │   │   ├── fw-question-1443197278.mbx
│   │   │   │   │   ├── mailpile-1398950855.mbx
│   │   │   │   │   ├── mailpile-1400069992.mbx
│   │   │   │   │   └── no-subject-1400067892.mbx
│   │   │   │   ├── new/
│   │   │   │   │   └── .gitkeep
│   │   │   │   └── tmp/
│   │   │   │       └── .gitkeep
│   │   │   ├── contacts/
│   │   │   │   ├── README
│   │   │   │   ├── contacttest.ldif
│   │   │   │   ├── contacttest.mork
│   │   │   │   └── contacttest.vcf
│   │   │   ├── gpg-keyring/
│   │   │   │   ├── pubring.gpg
│   │   │   │   ├── secring.gpg
│   │   │   │   ├── testing.gpg.pub
│   │   │   │   └── trustdb.gpg
│   │   │   ├── pgp-data/
│   │   │   │   ├── buildexamples.py
│   │   │   │   └── sources/
│   │   │   │       ├── ar.iso-8859-6
│   │   │   │       ├── ar.utf-8
│   │   │   │       ├── ar.windows-1256
│   │   │   │       ├── cn.utf-8
│   │   │   │       ├── gr.iso-8859-7
│   │   │   │       ├── gr.utf-8
│   │   │   │       ├── gr.windows-1253
│   │   │   │       ├── ru.koi8-ru
│   │   │   │       ├── ru.utf-8
│   │   │   │       ├── ru.windows-1251
│   │   │   │       └── vi.utf-8
│   │   │   ├── pub.key
│   │   │   └── tests.mbx
│   │   ├── gui/
│   │   │   ├── __init__.py
│   │   │   ├── test_contacts.py
│   │   │   ├── test_mail.py
│   │   │   └── test_tags.py
│   │   ├── test_command.py
│   │   ├── test_config.py
│   │   ├── test_crypto_policy.py
│   │   ├── test_eventlog.py
│   │   ├── test_keylookup.py
│   │   ├── test_mail_generator.py
│   │   ├── test_mailutils.py
│   │   ├── test_performance.py
│   │   ├── test_plugin.py
│   │   ├── test_search.py
│   │   ├── test_ui.py
│   │   ├── test_vcard.py
│   │   └── test_vcard_mork.py
│   ├── ui.py
│   ├── urlmap.py
│   ├── util.py
│   ├── vcard.py
│   ├── vfs.py
│   ├── workers.py
│   └── www/
│       ├── __init__.py
│       ├── jinjaextensions.py
│       └── jinjaloader.py
├── mp.cmd
├── package.json
├── packages/
│   ├── Dockerfile_debian
│   ├── debian/
│   │   ├── changelog
│   │   ├── compat
│   │   ├── control
│   │   ├── copyright
│   │   ├── gbp.conf
│   │   ├── mailpile-apache2.dirs
│   │   ├── mailpile-apache2.install
│   │   ├── mailpile-apache2.links
│   │   ├── mailpile-apache2.lintian-overrides
│   │   ├── mailpile-apache2.postinst
│   │   ├── mailpile-apache2.postrm
│   │   ├── mailpile-desktop.install
│   │   ├── mailpile-desktop.links
│   │   ├── mailpile.install
│   │   ├── mailpile.lintian-overrides
│   │   ├── mailpile.manpages
│   │   ├── rules
│   │   ├── source/
│   │   │   └── format
│   │   └── watch
│   ├── docker/
│   │   └── entrypoint.sh
│   ├── macos/
│   │   ├── README.md
│   │   ├── appdmg.json.template
│   │   ├── brew/
│   │   │   └── symlinks.rb
│   │   ├── build-script/
│   │   │   ├── 00-check-dependencies
│   │   │   ├── 10-create-app-icons
│   │   │   ├── 11-build-gui-o-mac-tic
│   │   │   ├── 20-build-homebrew
│   │   │   ├── 21-install-mailpile-deps
│   │   │   ├── 30-slim-down
│   │   │   ├── 55-install-mailpile
│   │   │   ├── 90-fix-libraries
│   │   │   ├── 91-fix-paths
│   │   │   ├── 92-fix-python-launcher
│   │   │   ├── 98-codesign
│   │   │   └── 99-make-dmg
│   │   ├── build.sh
│   │   ├── configurator.sh
│   │   ├── dmgbuild-settings.py
│   │   └── mailpile
│   ├── mailpile.1
│   ├── redhat/
│   │   └── mailpile.spec.in
│   ├── scripts/
│   │   ├── build-controller.py
│   │   ├── build-deb.sh
│   │   ├── build-desktop.py
│   │   ├── crontab
│   │   ├── deb-package-py-deps.sh
│   │   ├── kite-runner-mac.cfg
│   │   ├── kite-runner-sample.cfg
│   │   ├── kite-runner-win.cfg
│   │   ├── kite-runner.py
│   │   ├── kiterunner.service.plist
│   │   └── update-repos.sh
│   └── windows-wix/
│       ├── README.QUICK_START.md
│       ├── README.md
│       ├── TODO.md
│       ├── assets/
│       │   └── LicenseText.rtf
│       ├── bin/
│       │   ├── launch-mailpile.bat
│       │   └── with-mailpile-env.py
│       ├── dependencies-windows.txt
│       ├── package.json
│       ├── package.py
│       ├── package_template.json
│       ├── provide/
│       │   ├── __init__.py
│       │   ├── __main__.py
│       │   ├── build.py
│       │   ├── cache.py
│       │   ├── default.py
│       │   └── scripts/
│       │       ├── __init__.py
│       │       ├── bootstrap.py
│       │       ├── export.py
│       │       ├── external.py
│       │       ├── extract_msi.py
│       │       ├── git_checkout.py
│       │       ├── msi_package.py
│       │       ├── python27.py
│       │       ├── root.py
│       │       ├── sign_tree.py
│       │       ├── util.py
│       │       ├── version.py
│       │       ├── win4gpg.py
│       │       └── zip.py
│       ├── provide-example-bootstrap-local.json
│       ├── provide-example-bootstrap-remote.json
│       ├── provide-example-fork-local.json
│       ├── provide-example-fork-remote.json
│       ├── provide.json
│       └── resources.json
├── requirements-dev.txt
├── requirements-with-deps.txt
├── requirements.txt
├── scripts/
│   ├── __init__.py
│   ├── add-gpgme-and-gnupg-to-venv
│   ├── clear-cache.sh
│   ├── colorprints.py
│   ├── compile-messages.sh
│   ├── create-debian-changelog.py
│   ├── docker-dev/
│   │   ├── down
│   │   ├── shell
│   │   └── up
│   ├── email-parsing-test.py
│   ├── gitwhere.sh
│   ├── gpg
│   ├── less-compiler.in
│   ├── mailpile
│   ├── mailpile-admin
│   ├── mailpile-decrypt.py
│   ├── mailpile-pyside.py
│   ├── mailpile-test.py
│   ├── make-messages.sh
│   ├── mbox-minimal.py
│   ├── minimize-pgp-key.py
│   ├── mk-credits.py
│   ├── nginx.conf
│   ├── python-lint.sh
│   ├── reset-mac-mailpile.sh
│   ├── setup-test.sh
│   ├── test-sendmail.sh
│   ├── unsent-mail-finder.py
│   └── version.py
├── setup.cfg
├── setup.py
├── shared-data/
│   ├── contrib/
│   │   ├── README.md
│   │   ├── autoajax/
│   │   │   ├── autoajax.js
│   │   │   └── manifest.json
│   │   ├── datadig/
│   │   │   ├── datadig-modal.html
│   │   │   ├── datadig.html
│   │   │   ├── datadig.js
│   │   │   ├── datadig.py
│   │   │   └── manifest.json
│   │   ├── demos/
│   │   │   ├── demos.css
│   │   │   ├── demos.js
│   │   │   ├── demos.py
│   │   │   ├── manifest.json
│   │   │   ├── md5sum-wordy.html
│   │   │   ├── md5sum.html
│   │   │   ├── md5sum.xml
│   │   │   └── search.html
│   │   ├── experiments/
│   │   │   ├── experiments.py
│   │   │   └── manifest.json
│   │   ├── forcegrapher/
│   │   │   ├── README.md
│   │   │   ├── d3.drasl.js
│   │   │   ├── d3.js
│   │   │   ├── forcegrapher.css
│   │   │   ├── forcegrapher.html
│   │   │   ├── forcegrapher.js
│   │   │   ├── forcegrapher.py
│   │   │   └── manifest.json
│   │   ├── hacks/
│   │   │   ├── hacks.py
│   │   │   └── manifest.json
│   │   ├── hints/
│   │   │   ├── hints/
│   │   │   │   ├── autotagging.html
│   │   │   │   ├── backups.html
│   │   │   │   ├── deletion.html
│   │   │   │   ├── dragging-tags.html
│   │   │   │   ├── gravatar.html
│   │   │   │   ├── keyboard-shortcuts.html
│   │   │   │   ├── organize-sidebar.html
│   │   │   │   └── spam.html
│   │   │   ├── hints.html
│   │   │   ├── hints.js
│   │   │   ├── hints.py
│   │   │   └── manifest.json
│   │   ├── i18nhelper/
│   │   │   ├── i18nhelper.js
│   │   │   ├── i18nhelper.py
│   │   │   └── manifest.json
│   │   ├── maildeck/
│   │   │   ├── maildeck.css
│   │   │   ├── maildeck.html
│   │   │   ├── maildeck.js
│   │   │   ├── maildeck.py
│   │   │   └── manifest.json
│   │   ├── remoteaccess/
│   │   │   ├── manifest.json
│   │   │   └── settings-remote.html
│   │   └── unthread/
│   │       ├── i18n-stubs.html
│   │       ├── manifest.json
│   │       ├── modal.html
│   │       └── unthread.js
│   ├── default-theme/
│   │   ├── README.md
│   │   ├── css/
│   │   │   ├── default.css
│   │   │   ├── guide.css
│   │   │   └── print.css
│   │   ├── html/
│   │   │   ├── abortabortabort/
│   │   │   │   └── index.html
│   │   │   ├── auth/
│   │   │   │   ├── login/
│   │   │   │   │   └── index.html
│   │   │   │   ├── logout/
│   │   │   │   │   └── index.html
│   │   │   │   └── shared.html
│   │   │   ├── backup/
│   │   │   │   └── restore/
│   │   │   │       └── index.html
│   │   │   ├── browse/
│   │   │   │   └── index.html
│   │   │   ├── contacts/
│   │   │   │   ├── add/
│   │   │   │   │   └── index.html
│   │   │   │   ├── import/
│   │   │   │   │   └── index.html
│   │   │   │   ├── index.html
│   │   │   │   └── view/
│   │   │   │       └── index.html
│   │   │   ├── crypto/
│   │   │   │   ├── gpg/
│   │   │   │   │   └── key/
│   │   │   │   │       └── index.html
│   │   │   │   └── tls/
│   │   │   │       └── getcert/
│   │   │   │           └── index.html
│   │   │   ├── filter/
│   │   │   │   └── list/
│   │   │   │       └── index.html
│   │   │   ├── group/
│   │   │   │   ├── add/
│   │   │   │   │   └── index.html
│   │   │   │   └── index.html
│   │   │   ├── help/
│   │   │   │   ├── bottom.html
│   │   │   │   └── index.html
│   │   │   ├── jsapi/
│   │   │   │   ├── app.js
│   │   │   │   ├── compose/
│   │   │   │   │   ├── attachments.js
│   │   │   │   │   ├── autosave.js
│   │   │   │   │   ├── body.js
│   │   │   │   │   ├── complete.js
│   │   │   │   │   ├── crypto.js
│   │   │   │   │   ├── events.js
│   │   │   │   │   ├── init.js
│   │   │   │   │   ├── modals.js
│   │   │   │   │   ├── recipients.js
│   │   │   │   │   └── tooltips.js
│   │   │   │   ├── contacts/
│   │   │   │   │   ├── display_modes.js
│   │   │   │   │   ├── events.js
│   │   │   │   │   ├── init.js
│   │   │   │   │   └── modals.js
│   │   │   │   ├── crypto/
│   │   │   │   │   ├── events.js
│   │   │   │   │   ├── find.js
│   │   │   │   │   ├── import.js
│   │   │   │   │   ├── init.js
│   │   │   │   │   ├── modals.js
│   │   │   │   │   ├── tooltips.js
│   │   │   │   │   └── ui.js
│   │   │   │   ├── global/
│   │   │   │   │   ├── activities.js
│   │   │   │   │   ├── eventlog.js
│   │   │   │   │   ├── global.js
│   │   │   │   │   ├── helpers.js
│   │   │   │   │   └── silly.js
│   │   │   │   ├── index.css
│   │   │   │   ├── index.html
│   │   │   │   ├── index.js
│   │   │   │   ├── index.txt
│   │   │   │   ├── message/
│   │   │   │   │   ├── events.js
│   │   │   │   │   ├── html-sandbox.js
│   │   │   │   │   ├── init.js
│   │   │   │   │   ├── message.js
│   │   │   │   │   ├── tooltips.js
│   │   │   │   │   └── ui.js
│   │   │   │   ├── pwa/
│   │   │   │   │   ├── manifest.json
│   │   │   │   │   └── service-worker.js
│   │   │   │   ├── search/
│   │   │   │   │   ├── bulk_actions.js
│   │   │   │   │   ├── display_modes.js
│   │   │   │   │   ├── events.js
│   │   │   │   │   ├── init.js
│   │   │   │   │   ├── selection_actions.js
│   │   │   │   │   ├── tooltips.js
│   │   │   │   │   └── ui.js
│   │   │   │   ├── settings/
│   │   │   │   │   └── content.js
│   │   │   │   ├── tags/
│   │   │   │   │   ├── events.js
│   │   │   │   │   ├── init.js
│   │   │   │   │   └── modals.js
│   │   │   │   ├── templates/
│   │   │   │   │   ├── modal-add-tag.html
│   │   │   │   │   ├── modal-auto.html
│   │   │   │   │   ├── modal-compose-quoted-reply.html
│   │   │   │   │   ├── modal-composer-encryption-helper.html
│   │   │   │   │   ├── modal-contact-add.html
│   │   │   │   │   ├── modal-display-keybindings.html
│   │   │   │   │   ├── modal-related-search.html
│   │   │   │   │   ├── modal-save-search.html
│   │   │   │   │   ├── modal-search-keyservers.html
│   │   │   │   │   ├── modal-send-public-key.html
│   │   │   │   │   ├── modal-tag-picker.html
│   │   │   │   │   ├── modal-upload-key.html
│   │   │   │   │   └── template-modal-tag-picker-item.html
│   │   │   │   └── ui/
│   │   │   │       ├── content.js
│   │   │   │       ├── events.js
│   │   │   │       ├── global.js
│   │   │   │       ├── init.js
│   │   │   │       ├── keybindings.js
│   │   │   │       ├── notifications.js
│   │   │   │       ├── selection.js
│   │   │   │       ├── sidebar.js
│   │   │   │       ├── tagging.js
│   │   │   │       ├── terminal.js
│   │   │   │       ├── tooltips.js
│   │   │   │       └── topbar.js
│   │   │   ├── layouts/
│   │   │   │   ├── auth.html
│   │   │   │   ├── content-tall.html
│   │   │   │   ├── content-wide.html
│   │   │   │   ├── content.html
│   │   │   │   ├── full-tall.html
│   │   │   │   ├── full-wide.html
│   │   │   │   ├── full.html
│   │   │   │   ├── minimal-tall.html
│   │   │   │   ├── minimal-wide.html
│   │   │   │   └── minimal.html
│   │   │   ├── logs/
│   │   │   │   ├── events/
│   │   │   │   │   └── index.html
│   │   │   │   ├── layout.html
│   │   │   │   └── network/
│   │   │   │       └── index.html
│   │   │   ├── message/
│   │   │   │   ├── compose/
│   │   │   │   │   └── index.html
│   │   │   │   ├── download/
│   │   │   │   │   └── index.html
│   │   │   │   ├── draft/
│   │   │   │   │   └── index.html
│   │   │   │   ├── forward/
│   │   │   │   │   └── forward.html
│   │   │   │   ├── index.html
│   │   │   │   ├── reply/
│   │   │   │   │   ├── composer.html
│   │   │   │   │   └── index.html
│   │   │   │   ├── send/
│   │   │   │   │   └── index.html
│   │   │   │   ├── single.html
│   │   │   │   └── update/
│   │   │   │       └── index.html
│   │   │   ├── page/
│   │   │   │   ├── contribute/
│   │   │   │   │   ├── bugs.html
│   │   │   │   │   └── index.html
│   │   │   │   ├── entropy/
│   │   │   │   │   └── index.html
│   │   │   │   ├── gmail-2-step-verification/
│   │   │   │   │   └── index.html
│   │   │   │   ├── gmail-access-non-google-accounts/
│   │   │   │   │   └── index.html
│   │   │   │   ├── index.html
│   │   │   │   ├── multipile/
│   │   │   │   │   └── index.html
│   │   │   │   ├── release-notes/
│   │   │   │   │   ├── credits-code.html
│   │   │   │   │   ├── credits-i18n.html
│   │   │   │   │   ├── credits.html
│   │   │   │   │   ├── index.html
│   │   │   │   │   └── license.html
│   │   │   │   └── template-straw-man/
│   │   │   │       ├── index.html
│   │   │   │       ├── index.js
│   │   │   │       └── tags.html
│   │   │   ├── partials/
│   │   │   │   ├── compose.html
│   │   │   │   ├── error_message_missing.html
│   │   │   │   ├── errors_content.html
│   │   │   │   ├── head.html
│   │   │   │   ├── helpers.html
│   │   │   │   ├── hidden.html
│   │   │   │   ├── javascript.html
│   │   │   │   ├── modals.html
│   │   │   │   ├── pile_compose.html
│   │   │   │   ├── pile_email_hints.html
│   │   │   │   ├── pile_email_tags.html
│   │   │   │   ├── pile_message.html
│   │   │   │   ├── pile_threading.html
│   │   │   │   ├── search_item.html
│   │   │   │   ├── sidebar.html
│   │   │   │   ├── tag_add.html
│   │   │   │   ├── thread_message_cryptofail.html
│   │   │   │   ├── tools_contacts.html
│   │   │   │   ├── tools_default.html
│   │   │   │   ├── tools_message.html
│   │   │   │   ├── tools_search.html
│   │   │   │   ├── tools_settings.html
│   │   │   │   ├── tools_tags.html
│   │   │   │   ├── tooltips.html
│   │   │   │   └── topbar.html
│   │   │   ├── plugins/
│   │   │   │   └── index.html
│   │   │   ├── profiles/
│   │   │   │   ├── account-form.html
│   │   │   │   ├── add/
│   │   │   │   │   └── index.html
│   │   │   │   ├── edit/
│   │   │   │   │   └── index.html
│   │   │   │   ├── index.html
│   │   │   │   └── remove/
│   │   │   │       └── index.html
│   │   │   ├── quitquitquit/
│   │   │   │   └── index.html
│   │   │   ├── search/
│   │   │   │   ├── atts.html
│   │   │   │   ├── default.html
│   │   │   │   ├── drafts.html
│   │   │   │   ├── index.html
│   │   │   │   ├── outbox.html
│   │   │   │   ├── outgoing.html
│   │   │   │   ├── photos.html
│   │   │   │   ├── prev_more_next.html
│   │   │   │   ├── sent.html
│   │   │   │   └── trash.html
│   │   │   ├── settings/
│   │   │   │   ├── index.html
│   │   │   │   ├── mailbox/
│   │   │   │   │   └── index.html
│   │   │   │   ├── plugins.html
│   │   │   │   ├── preferences.html
│   │   │   │   ├── privacy.html
│   │   │   │   ├── recipes.html
│   │   │   │   └── set/
│   │   │   │       ├── index.html
│   │   │   │       └── password/
│   │   │   │           ├── index.html
│   │   │   │           └── keys.html
│   │   │   ├── setup/
│   │   │   │   ├── oauth2/
│   │   │   │   │   └── index.html
│   │   │   │   ├── password/
│   │   │   │   │   └── index.html
│   │   │   │   └── welcome/
│   │   │   │       └── index.html
│   │   │   └── tags/
│   │   │       ├── add/
│   │   │       │   └── index.html
│   │   │       ├── edit.html
│   │   │       ├── form.html
│   │   │       ├── index.html
│   │   │       └── sidebar.html
│   │   ├── index.html
│   │   ├── js/
│   │   │   ├── helpers.js
│   │   │   ├── libraries.js
│   │   │   └── mousetrap.global.bind.js
│   │   ├── less/
│   │   │   ├── app/
│   │   │   │   ├── attachments.less
│   │   │   │   ├── compose.less
│   │   │   │   ├── contacts.less
│   │   │   │   ├── crypto.less
│   │   │   │   ├── files.less
│   │   │   │   ├── global.less
│   │   │   │   ├── helpers.less
│   │   │   │   ├── icons.less
│   │   │   │   ├── library-override.less
│   │   │   │   ├── login.less
│   │   │   │   ├── message.less
│   │   │   │   ├── mobile.less
│   │   │   │   ├── modals.less
│   │   │   │   ├── navigation.less
│   │   │   │   ├── notifications.less
│   │   │   │   ├── pile.less
│   │   │   │   ├── profiles.less
│   │   │   │   ├── screens.less
│   │   │   │   ├── search.less
│   │   │   │   ├── settings.less
│   │   │   │   ├── setup.less
│   │   │   │   ├── sidebar.less
│   │   │   │   ├── tablet.less
│   │   │   │   ├── tags.less
│   │   │   │   ├── terminal.less
│   │   │   │   ├── thread.less
│   │   │   │   ├── tooltips.less
│   │   │   │   ├── topbar.less
│   │   │   │   └── webfonts.less
│   │   │   ├── config.less
│   │   │   ├── default.less
│   │   │   ├── libraries/
│   │   │   │   ├── bootstrap.less
│   │   │   │   ├── dropdowns.less
│   │   │   │   ├── modals.less
│   │   │   │   └── typeahead.less
│   │   │   └── print.less
│   │   ├── theme.json
│   │   └── webfonts/
│   │       ├── LICENSE
│   │       └── index.html
│   ├── locale/
│   │   ├── README.md
│   │   └── mailpile.pot
│   ├── mailpile-gui/
│   │   ├── icons-osx/
│   │   │   └── mk_icons.sh
│   │   ├── mailpile-gui.py
│   │   ├── mailpile.desktop
│   │   └── media/
│   │       └── background.xcf
│   └── multipile/
│       ├── README.md
│       ├── mailpile-admin.py
│       ├── mailpile-launcher.py
│       ├── multipile.rc.sample
│       └── www/
│           ├── apache-broken.html
│           ├── index.html
│           └── not-running.html
├── test-requirements.txt
└── tox.ini

================================================
FILE CONTENTS
================================================

================================================
FILE: .coveragerc
================================================
# =========================================================================
# COVERAGE CONFIGURATION FILE: .coveragerc
# =========================================================================
# LANGUAGE: Python
# SEE ALSO:
#  * http://nedbatchelder.com/code/coverage/
#  * http://nedbatchelder.com/code/coverage/config.html
# =========================================================================

[coverage:run]
append  = .coverage
include = mailpile*
omit = nose
branch  = True
#parallel = True

[coverage:report]
# Regexes for lines to exclude from consideration
exclude_lines =
    # Have to re-enable the standard pragma
    pragma: no cover

    # Don't complain about missing debug-only code:
    def __repr__
    if self\.debug

    # Don't complain if tests don't hit defensive assertion code:
    raise AssertionError
    raise NotImplementedError

    # Don't complain if non-runnable code isn't run:
    if 0:
    if False:
    if __name__ == .__main__.:

ignore_errors = True

[coverage:html]
directory = build/coverage.html

[coverage:xml]
outfile = build/coverage.xml



================================================
FILE: .dockerignore
================================================
Dockerfile
Vagrantfile


================================================
FILE: .gitignore
================================================
*.pyc
*.pyo
*~
*.po
*.debhelper
*.iml
*.ipr
*.swp
*.swo
mp-virtualenv
.idea
.*deps
.tox
mailpile.egg-info/
mailpile-tmp.py
static/default/plugins/
mailpile/tests/data/private-test-data.mbx
mailpile/tests/data/tmp
mailpile/tests/data/gpg-keyring/random_seed
scripts/less-compiler.mk
.DS_Store
.vagrant
.coverage
ghostdriver.log
build/
dist/
.eggs/
cover/
bower_components/
node_modules/
testing/
mailpile.tar.gz
AUTHORS
ChangeLog
shared-data/multipile/www/admin.cgi
setup-tmp/


================================================
FILE: .gitmodules
================================================
[submodule "doc"]
	path = doc
	url = https://github.com/pagekite/Mailpile.wiki.git
	branch = master
[submodule "shared-data/contrib/print"]
	path = shared-data/contrib/print
	url = https://github.com/mailpile/Mailpile-print.git
[submodule "submodules/gui-o-matic"]
	path = submodules/gui-o-matic
	url = https://github.com/mailpile/gui-o-matic
[submodule "submodules/gui-o-mac-tic"]
	path = submodules/gui-o-mac-tic
	url = https://github.com/mailpile/gui-o-mac-tic


================================================
FILE: .travis.yml
================================================
language: python
python: 2.7
env:
  - TOX_ENV=py27

addons:
  apt:
    packages:
      - gnupg

install:
  - pip install tox coveralls

script:
  - tox -e $TOX_ENV

after_success:
  - coveralls


================================================
FILE: .tx/config
================================================
[main]
host = https://www.transifex.com

[mailpile.mailpilepot]
source_file = shared-data/locale/mailpile.pot
source_lang = en
type = PO
file_filter = shared-data/locale/<lang>/LC_MESSAGES/mailpile.po


================================================
FILE: AGPLv3.txt
================================================
NOTE: Please see the file COPYING.md for details on Mailpile licensing.


                    GNU AFFERO GENERAL PUBLIC LICENSE
                       Version 3, 19 November 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.

  A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate.  Many developers of free software are heartened and
encouraged by the resulting cooperation.  However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.

  The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community.  It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server.  Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.

  An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals.  This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU Affero General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Remote Network Interaction; Use with the GNU General Public License.

  Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software.  This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time.  Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source.  For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code.  There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# The Mailpile Code of Conduct

The Mailpile project depends on its community of volunteers, supporters and of
course users. We want people to feel welcome and respected. To this end,
project maintainers have plegded to adhere to and enforce the Contrbitor
Covenant Code of Conduct, in their Mailpile related activities, both online and
in person.

We respectfully ask that other contributors and participants in our community
do so as well.

Thank you for your understanding, and welcome!


# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
  advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
  address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at <mailto:team@mailpile.is>. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: CONTRIBUTING.md
================================================
## How to contribute to Mailpile

First of all: Thank you! :heart:


#### Further Reading

Second of all, please adhere to our Code of Conduct when you participate
in our community. Be kind, be respectful. [The full Code of Conduct can
be found here](CODE_OF_CONDUCT.md).

Please also be sure you are comfortable with [our license](COPYING.md)
before contributing any code to Mailpile.

Note that Mailpile is a slightly unsual app; the design is both political
and opinionated. If you are confused or unsure whether something is a bug or a
feature, [our developer FAQ might have answers](DEV_FAQ.md)! Please read it
before filing issues about design choices or attempting to reorganize the code
in any major way. The FAQ also contains a quick introduction Mailpile internals
and debugging techniques.

Also, have you seen [the main Mailpile website](https://www.mailpile.is/) and
[the community Discourse](https://community.mailpile.is/categories)?


#### Are you new to Mailpile and/or FOSS?

* Welcome! The first step is probably to get Mailpile up and running, play with it, get familiar.

* Read the links above, and [check out our website](https://www.mailpile.is/) if you haven't already.

* Not sure what to work on? Find inspiration in one of our [Low Hanging Fruit](https://github.com/mailpile/Mailpile/issues?q=is%3Aissue+is%3Aopen+label%3A%22Low+Hanging+Fruit%22) issues!

* Otherwise, read on...


#### Did you find a bug?

* **Ensure the bug was not already reported** by searching on GitHub
  under [Issues](https://github.com/mailpile/Mailpile/issues).

* If you're unable to find an open issue addressing the problem,
  [open a new one](https://github.com/mailpile/Mailpile/issues/new). Be
  sure to include a **title and clear description**, as much relevant
  information as possible.

* Remember, we cannot read your mind or see your screen, so you'll
  probably need to answer all of these:

   1. What were you doing?
   2. What did you expect would happen?
   3. What actually happened?
   4. Operating system? GnuPG version? Mailpile version?

* Reproducability is key. If you cannot reliably trigger the bug or
  cannot describe how to do so, then unfortunately it's less likely that
  the Mailpile team will be able to do anything about it. It may still
  be useful to file a report in case others are having the same issue, but
  bugs that can be reproduced will in general get fixed much faster!

* The [Developer FAQ](DEV_FAQ.md) has a section on debugging techniques.

* If it's not bug, but you still need help: [visit our Discourse support
  forum](https://community.mailpile.is/c/support).


#### Did you write a patch that fixes a bug?

* Open a new GitHub pull request with the patch.

* Ensure the PR description clearly describes the problem and solution.
  Include the relevant issue number if applicable.


#### Do you plan write a patch that adds a feature?

* Please be sure you have read the [Developer FAQ](DEV_FAQ.md) to ensure
  your feature is compatible with our high-level goals and design decisions.

* If you are unsure or would like some guidance, join the #mailpile
  channel on IRC (Freenode) and discuss your plans there.

* Open a new GitHub pull request with the patch.

* Ensure the PR description clearly describes what your feature does.
  Include the relevant issue number if applicable.


#### Did you fix whitespace, format code, or make a purely cosmetic patch?

Changes that are cosmetic in nature and do not add anything substantial to the
stability, functionality, or testability of Mailpile will generally not be
accepted.

All patches to Mailpile are reviewed by a human and our resources (time,
people) are very limited. Cosmetic patches are no easier to review than other
patches and we would rather focus our efforts on functional improvements to the
software.


#### Do you want help other users?

Our [community Discourse](https://community.mailpile.is/categories) has a
support forum and needs friendly people to help out and steer conversations
in friendly and productive directions. Please join the conversation!


#### Do you want to translate Mailpile to your language?

Please feel free to join our translation community
[on Transifex](https://www.transifex.com/otf/mailpile/).


#### Do you plan to write documentation?

* Please feel free to contribute documentation to
  [our wiki](https://github.com/mailpile/Mailpile/wiki).

* If you would like some guidance, join the #mailpile channel on IRC
  (Freenode) or file an issue with your questions.

* If you added a new page or made major major changes to an existing page,
  please file [a new issue requesting a review](https://github.com/mailpile/Mailpile/issues/new)
  when you are done. Be sure to include a link to the wiki page!


#### Credits

This document borrows some language and structure from the [Ruby on Rails
contributor guidelines](https://raw.githubusercontent.com/rails/rails/master/CONTRIBUTING.md).



================================================
FILE: COPYING.md
================================================
# Mailpile - a program for doing stuff with e-mail

Copyright (C) 2011-2015, Bjarni R. Einarsson, Mailpile ehf and friends.

This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero
General Public License more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.


## What happened to the Apache license?

This software used to be dual-licensed under AGPLv3 and the Apache
License 2.0. If you feel an uncontrollable urge to fork and carry on
without the AGPL, the last dual-licensed code point is tagged as
"Gunsmoke--TheLastApacheTag".  Have fun!

For more details on our licensing choices, see these blog posts:

* <https://www.mailpile.is/blog/2015-05-08_Choosing_a_License.html>
* <https://www.mailpile.is/blog/2015-06-15_Community_License_Feedback.html> 
* <https://www.mailpile.is/blog/2015-07-02_Licensing_Decision.html>

Thanks!


================================================
FILE: DEV_FAQ.md
================================================
## Mailpile Developer FAQ

This document contain a collection of frequently asked questions (with
answers) about Mailpile development. Please familiarize yourself with
the contents before attempting any deep hacking on Mailpile.

You don't have to agree with all of our priorities to take part or make
use of Mailpile, but we do feel it helps if most of the community is
rowing in roughly the same general direction!

Note: If you are just looking for debugging tips and tricks, you can skip
to the end.


### Why Mailpile?

The long-term goal of Mailpile is to help *non-technical people* become
*more independent* and *more private* online, in particular when it
comes to e-mail.

By **more independent**, we mean people should be in control of their
e-mail and the software used to manage it. This is why Mailpile is Free
Software, and this is why we *don't* promote Mailpile as a tool for
building "cloud services".

By **more private**, we mean people should have more control over who has
access to their e-mail, when and how. This is why Mailpile tries to make
e-mail encryption more accessible, and this is why Mailpile tries to
make it convenient *and secure* for people to store their e-mail on their
own devices.

Our focus on **non-technical people** implies, amongst other things, that
we cannot exclude people who are using non-free platforms such as
Microsoft Windows or Mac OS X, and we cannot require our users learn a
radically new, unfamiliar user interface or understand highly technical
concepts such as public key cryptography.


### Are All of These Decisions Awesome?

No. Some of these decisions were almost certainly mistakes. But that's
part of innovating, you try new things and see how they go!

So we kindly request that our contributors and collaboratores refrain
from picking arguments with us over these decisions. Making forward
progress is much more fun than rehashing the past over and over again.


### Why Do You Reinvent So Many Wheels?

Usually, the answer is one or more of the following:

   * We didn't know a solution already existed
   * We evaluated solutions X, Y and Z, but didn't like them
   * We felt the problem was simple enough to Just Do It

In general, we are reluctant to take on new external dependencies unless
they are stable and widely available: if they exist in pre-packaged form
for Windows, Mac OS X and the most common Linux distributions, they are
probably fine!

If not, the benefits need to outweigh the added complexity posed for our
cross-platform packaging efforts.

For this reason we also tend to prefer pure-Python (or Javascript, for
the UI) libraries over native code, which also avoids certain classes of
bugs and security vulnerabilities which are simply not present in a
memory-safe, managed language.

We have broken this rule more often than we'd like in our user-interface
code, due to cultural differences between web developers and Python
folks. *We would like to gradually reduce our front-end dependencies.*


### Why a Search Engine?

Mailpile started as an experimental search engine, a hobby project.
Everything else came later.

*OK, so why not replace it with a standard component?*

Because it works! Replacing it at this point would be *more work, not less*.

Also, we are unaware of any "off the shelf" search engines that let us
encrypt their data stores and we feel encrypting the search index is an
absolute requirement since we by default allow the user to search inside
encrypted messages.

*I see. Why not remove it, to simplify the code?*

The entire app is built around the search metaphor, much like Google's
GMail. This is a fundamentally different way to build an e-mail client,
from the traditional "messages and mailboxes" model.

The search engine also makes it easy for Mailpile to do some pretty cool
things. For example:

   * Mailpile can evaluate the trustworthiness of a message by asking the
     search engine about the past behaviour of the sender
   * Mailpile can postpone processing of things like attached PGP keys
     until it actually intends to use the key: the search engine makes the
     keys easy to find later on


### Why doesn't Mailpile have a Native User Interface?

For now, because the team is very small and we would like to reach users
on many different platforms, we have bet entirely on the web as our
primary user interface:

   * This decision allows us to target any operating system for which
     Python 2.7 has been made available.

   * It also allows us to get help from the enormous community of web
     developers; this is a much larger talent pool than the pool of
     developers that know how to write native apps.

   * Last but not least, the web interface is key to our plan to allow
     users to access their Mailpile remotely over the network. Remote
     access is critical if we want to get people to store their e-mail
     on their own devices, because most people read their mail on
     multiple devices (laptop, tablet, mobile, work computer, etc).

That said: we do want to have a minimal native user interface, on all
the major desktop operating systems. [That is what the gui-o-matic
spin-off project is about](https://github.com/mailpile/gui-o-matic).

Native mobile apps on Android and/or iOS would also be nice to have!


### How Does Mailpile Handle Security?

This is a huge topic! Please consult our [Security
Roadmap](https://github.com/mailpile/Mailpile/wiki/Security-roadmap).

Security is complex and means different things to different people.

The most controversial questions relating to security, have to do with
mass surveillance and law enforcement. Mailpile's stance is that we
believe that people have an innate right to privacy we believe that mass
surveillance is *wrong*. If the government wants to read your e-mail,
we feel they should present you with a search warrant.

Thwarting other adversaries (criminals, jealous partners, etc.) is also
very much something we care about, but is probably less divisive.


### Why GnuPG? Why not GPGME? Why not PGPy?

GnuPG is mature and stable. Although the user interfaces may leave
something to be desired, it has a rich ecosystem of powerful tools built
around it and a wealth of documentation and support to be found online.
If we didn't use GnuPG, we would have to reinvent a lot of wheels that
aren't central to Mailpile's mission.

Our issue tracker contains [further discussions on the topic of why
GnuPG and not something else, such as
PGPy](https://github.com/mailpile/Mailpile/issues/1743).

[Use of GPGME is also being
discussed](https://github.com/mailpile/Mailpile/issues/1742). Currently,
we don't feel GPGME provides enough benefit to justify the additional
dependency and the additional (hypothetical) risk posed by solving the
GnuPG integration problem with a large amount of code written in C (as
opposed to Python, which is memory-safe and less likely to contain
certain classes of security vulnerabilities).


### Why not Python 3?

We depend on some libraries - spambayes in particular - which were not
available for Python 3 when we started this project.

We don't think Python 2 is going away in the near future.

[There is a Github issue discussing
this.](https://github.com/mailpile/Mailpile/issues/160)


### Why Not Django? Or Flask?

Reasons!

Our way may be unusual, but it's kinda awesome once you get used to it
and it wasn't obvious to us how we could get this kind of behaviour from
one of the standard frameworks.

Please read the next section for details.


### How Does Mailpile's Web UI Work?

The text-based command-line interface is an important part of Mailpile's
user interface. Our home-brewed framework allows us to generate web API
end-points, text commands and command-line arguments *at the same time*.

Our internal framework also has the concept of commands supporting
multiple output formats; so the same API endpoint can generate text,
templated HTML, JSON and XML-RPC interfaces with relatively little
additional code. Some endpoints also generate XML, CSV, CSS or
Javascript.

You can try this yourself, simply by editing the URL in your browser:

    # The default, rendered as HTML
    http://localhost:33411/in/inbox/
    http://localhost:33411/in/inbox/as.html
    http://localhost:33411/search/?q=in:inbox

    # Same thing, as JSON
    http://localhost:33411/in/inbox/as.json
    http://localhost:33411/api/0/search/?q=in:inbox

    # Same thing, as text
    http://localhost:33411/in/inbox/as.text
    http://localhost:33411/api/0/search/as.text?q=in:inbox

The filename part of the URL is used to select output formats. All
endpoints support `as.json`, most support HTML and/or text.

The HTML output of each command is generated using Jinja2 templates
that are found in `shared-data/default-theme/html/...`. The directory
structure generally matches the URL paths seen in the browser, with
the main template for each command named `index.html`.

Alternate templates for the same API endpoint can have other names, for
example a template named `.../html/search/social.html` would be
accessible using URLs like so:

    http://localhost:33411/in/inbox/social.html
    http://localhost:33411/search/social.html?q=from:person@foo.com


### Can I Develop Plugins For Mailpile?

Sort-of!

Internally, the app is quite modular and there are methods which allow
code to register classes or functions that perform various functions.

However, the plugin API is not considered stable, it is incomplete and
it is not very well documented. It may also not be a very nice API, and
we rather expect it to develop and change rapidly post-1.0.

If you are interested in Mailpile's plugin APIs, take a look in
`shared-data/contrib/` for some examples of "external plugins" and
`mailpile/plugins/` for "internal plugins".


### How Do I Debug Mailpile?

Developers should learn to use the Mailpile CLI. The `mailpile>` prompt is
where all of the low-level magic happens. Future versions of Mailpile will
expose this functionality to the web interface itself, but for now you will
need to use your shell.

Possibly the most important command for Mailpile hackers, is to know
how to enable debugging. An example:

    # Enable verbose debugging of HTTP requests and GnuPG integration
    # Note: HTTP debugging disables all sorts of internal caches!
    mailpile> set sys.debug = log http gnupg
    ...

Many other subsystems can have debugging enabled.  At the time of writing,
the `sys.debug` can include the following terms to make various parts of
the app more verbose:

    log http compose cryptostate autotag rescan keywords cache connbroker
    vcard pop3 gnupg keylookup imap sources jinja timing sendmail httpdata

There are also a few other ways to examine the app state:

    # Watch logging and debug messages fly by
    mailpile> eventlog/watch
    ...
    [CTRL+C]
    ...

    # Examine event log (piped through less)
    mailpile> pipe less eventlog
    ...
    mailpile> pipe less eventlog incomplete
    ...

    # Get an overview of what threads are running and what they are doing
    mailpile> ps
    ...

Low-level changes and exploration of the configuration are also best done
from the CLI:

    # Explore the configuration; see also mailpile/config/defaults.py
    mailpile> print -short sys
    ...
    mailpile> print -flat sources
    ...
    mailpile> print -secrets secrets
    ...
 
    # Change things (dangerous)
    mailpile> set sys.gpg_binary = /bin/false
    ...

    # Reset something to its default setting
    mailpile> unset sys.gpg_binary
    ...

There is also a help command, and you can use tab completion to try and
"guess" what commands exist.

    mailpile> help
    ...
    mailpile> help tags
    ...

Finally, the app ships with a `hacks` plugin which is disabled by default.
If you load it, that will add a few more low-level commands, including an
embedded Python interpretor:

    mailpile> plugins/load hacks
    ...

    mailpile> hacks/pycli
    ...

There's sure to be more; please feel free to file a pull request against
this document to add your favourite tricks or clarify these.


================================================
FILE: Dockerfile
================================================
FROM debian:stretch-slim

ENV GID 33411
ENV UID 33411

RUN apt-get update && \
    apt-get install -y curl apt-transport-https gnupg && \
    curl -s https://packages.mailpile.is/deb/key.asc | apt-key add - && \
    echo "deb https://packages.mailpile.is/deb release main" | tee /etc/apt/sources.list.d/000-mailpile.list && \
    apt-get update && \
    apt-get install -y mailpile && \
    # TODO Enable apache for multi users
    # apt-get install -y mailpile-apache2
    update-rc.d tor defaults && \
    service tor start && \
    groupadd -g $GID mailpile && \
    useradd -u $UID -g $GID -m mailpile && \
    su - mailpile -c 'mailpile setup' && \
    apt-get clean

WORKDIR /home/mailpile
USER mailpile

VOLUME /home/mailpile/.local/share/Mailpile
EXPOSE 33411

CMD mailpile --www=0.0.0.0:33411/ --wait


================================================
FILE: Dockerfile.dev
================================================
FROM mailpile

# Install C compiler for python deps w/ native extensions
RUN apk --no-cache add \
  gcc \
  libc-dev \
  python-dev \
  shadow

# Workaround: Setting mailpile users uid to 1000 to have write permissions
# from the docker host the to the shared volumes /Mailpile and /mailpile-data.
# Mounted volumes seem to be configured w/ uid/guid = 1000.
# Learn more here:
# - https://github.com/docker/docker/issues/7198
# - https://denibertovic.com/posts/handling-permissions-with-docker-volumes/
RUN usermod -u 1000 mailpile

RUN pip install -r requirements-dev.txt

RUN chmod +x /entrypoint.sh

CMD ["./mp"]


================================================
FILE: Gruntfile.js
================================================
module.exports = function(grunt) {

  grunt.registerTask('watch', [ 'watch' ]);

  grunt.initConfig({
    concat: {
      js: {
        options: {
          separator: ';'
        },
        src: [
          'bower_components/jquery/dist/jquery.min.js',
          'bower_components/underscore/underscore-min.js',
          'bower_components/jquery-timer/jquery.timer.js',
          'bower_components/autosize/dist/autosize.js',
          'bower_components/mousetrap/mousetrap.js',
          'shared-data/default-theme/js/mousetrap.global.bind.js',
          'bower_components/jquery.ui/ui/jquery.ui.core.js',
          'bower_components/jquery.ui/ui/jquery.ui.widget.js',
          'bower_components/jquery.ui/ui/jquery.ui.mouse.js',
          'bower_components/jquery.ui/ui/jquery.ui.draggable.js',
          'bower_components/jquery.ui/ui/jquery.ui.droppable.js',
          'bower_components/jquery.ui/ui/jquery.ui.sortable.js',
          'bower_components/jqueryui-touch-punch/jquery.ui.touch-punch.js',
          'bower_components/qtip2/basic/jquery.qtip.min.js',
          'bower_components/jquery-slugify/dist/slugify.js',
          'bower_components/typeahead.js/dist/typeahead.jquery.js',
          'bower_components/bootstrap/js/dropdown.js',
          'bower_components/bootstrap/js/modal.js',
          'bower_components/favico.js/favico.js',
          'bower_components/select2/select2.min.js',
          'bower_components/moxie/bin/js/moxie.min.js',
          'bower_components/plupload/js/plupload.min.js',
          'bower_components/dompurify/dist/purify.min.js'
        ],
        dest: 'shared-data/default-theme/js/libraries.min.js'
      }
    },
    uglify: {
      options: {
        mangle: false
      },
      js: {
        files: {
          'shared-data/default-theme/js/libraries.min.js': ['shared-data/default-theme/js/libraries.min.js']
        }
      }
    },
    less: {
      options: {
        cleancss: true
      },
      style: {
        files: {
          "shared-data/default-theme/css/default.css": "shared-data/default-theme/less/default.less"
        }
      }
    },
    watch: {
      js: {
        files: ['shared-data/default-theme/js/*.js'],
        tasks: ['concat:js', 'uglify:js'],
        options: {
          livereload: true,
        }
      },
      css: {
        files: [
          'shared-data/default-theme/less/config.less',
          'shared-data/default-theme/less/default.less',
          'shared-data/default-theme/less/app/*.less',
          'shared-data/default-theme/less/libraries/*.less'
        ],
        tasks: ['less:style'],
        options: {
          livereload: true,
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-less');
  grunt.loadNpmTasks('grunt-contrib-watch');

};


================================================
FILE: MANIFEST.in
================================================
recursive-exclude locale *
recursive-exclude scripts *
recursive-exclude testing *
recursive-include mailpile/tests/data *
recursive-exclude mailpile/tests/data *~
recursive-include mailpile/locale *.mo *.po *.pot
recursive-include mailpile/plugins *
include Makefile
include README.md
include AGPLv3.txt
include LICENSE-2.0.txt
include requirements.txt
include test-requirements.txt
include requirements-dev.txt
include install_hooks.py
include scripts/compile-messages.sh
include scripts/make-messages.sh
include scripts/mailpile-test.py
include scripts/test-sendmail.sh
include scripts/setup-test.sh
recursive-include shared-data *


================================================
FILE: Makefile
================================================
# Recipes for stuff
export PYTHONPATH := .

help:
	@echo ""
	@echo "BUILD"
	@echo "    dpkg"
	@echo "        Create a debian package of this service (in a Docker "
	@echo "        container)."
	@echo ""


all:	submodules alltests docs web compilemessages transifex

dev:
	@echo export PYTHONPATH=`pwd`

arch-dev:
	sudo pacman -Syu --needed community/python2-pillow extra/python2-lxml community/python2-jinja \
	                 community/python2-pep8 extra/python2-nose community/phantomjs \
	                 extra/python2-pip community/python2-mock \
	                 extra/ruby community/npm community/spambayes
	TMPDIR=`mktemp -d /tmp/aur.XXXXXXXXXX`; \
	cd $$TMPDIR; \
	pacman -Qs '^yuicompressor$$' > /dev/null; \
	if [ $$? -ne 0 ]; then \
	  sudo pacman -S --needed core/base-devel; \
	  curl -s https://aur.archlinux.org/cgit/aur.git/snapshot/yuicompressor.tar.gz | tar xzv; \
	  cd yuicompressor; \
	  makepkg -si; \
	  cd $$TMPDIR; \
	fi; \
	cd /tmp; \
	rm -rf $$TMPDIR
	sudo pip2 install 'selenium>=2.40.0'
	which lessc >/dev/null || sudo gem install therubyracer less
	which bower >/dev/null || sudo npm install -g bower
	which uglify >/dev/null || sudo npm install -g uglify

fedora-dev:
	sudo yum install python-imaging python-lxml python-jinja2 python-pep8 \
	                     ruby ruby-devel python-yui python-nose spambayes \
	                     phantomjs python-pip python-mock npm
	sudo yum install rubygems; \
	sudo yum install python-pgpdump || pip install pgpdump
	sudo pip install 'selenium>=2.40.0'
	which lessc >/dev/null || sudo gem install therubyracer less
	which bower >/dev/null || sudo npm install -g bower
	which uglify >/dev/null || sudo npm install -g uglify

debian-dev:
	sudo apt-get install python-imaging python-lxml python-jinja2 pep8 \
	                     ruby-dev yui-compressor python-nose spambayes \
	                     python-pip python-mock python-selenium \
						 rubygems-integration
	dpkg -l|grep -qP ' nodejs .*nodesource' || sudo apt install npm
	sudo apt-get install python-pgpdump || pip install pgpdump
	which phantomjs >/dev/null || sudo apt-get install phantomjs || sudo npm install -g phantomjs
	which lessc >/dev/null || sudo gem install therubyracer less
	which bower >/dev/null || sudo npm install -g bower
	which uglify >/dev/null || sudo npm install -g uglify


submodules:
	git submodule update --remote

docs: submodules
	@python2.7 mailpile/urlmap.py |grep -v ^FIXME: >doc/URLS.md
	@ls -l doc/URLS.md
	@python2.7 mailpile/defaults.py |grep -v -e ^FIXME -e ';timestamp' \
           >doc/defaults.cfg
	@ls -l doc/defaults.cfg

web: less js
	@true

alltests: clean pytests
	@chmod go-rwx mailpile/tests/data/gpg-keyring
	@DISPLAY= nosetests
	@DISPLAY= python2.7 scripts/mailpile-test.py || true
	@git checkout mailpile/tests/data/

pytests:
	@echo -n 'security         ' && python2.7 mailpile/security.py
	@echo -n 'urlmap           ' && python2.7 mailpile/urlmap.py -nomap
	@echo -n 'search           ' && python2.7 mailpile/search.py
	@echo -n 'mailboxes.mbox   ' && python2.7 mailpile/mailboxes/mbox.py
	@echo -n 'mailutils.safe   ' && python2.7 mailpile/mailutils/safe.py
	@echo -n 'mailutils.addrs  ' && python2.7 mailpile/mailutils/addresses.py
	@echo -n 'mailutils.emails ' && python2.7 mailpile/mailutils/emails.py
	@echo -n 'config/base      ' && python2.7 mailpile/config/base.py
	@echo -n 'config/validators' && python2.7 mailpile/config/validators.py
	@echo -n 'config/manager   ' && python2.7 mailpile/config/manager.py
	@echo -n 'conn_brokers     ' && python2.7 mailpile/conn_brokers.py
	@echo -n 'crypto/autocrypt ' && python2.7 mailpile/crypto/autocrypt.py
	@echo -n 'plug...autocrypt ' && python2.7 mailpile/plugins/crypto_autocrypt.py
	@echo -n 'crypto/mime      ' && python2.7 mailpile/crypto/mime.py
	@echo -n 'index.base       ' && python2.7 mailpile/index/base.py
	@echo -n 'index.msginfo    ' && python2.7 mailpile/index/msginfo.py
	@echo -n 'index.mailboxes  ' && python2.7 mailpile/index/mailboxes.py
	@echo -n 'index.search     ' && python2.7 mailpile/index/search.py
	@echo -n 'util             ' && python2.7 mailpile/util.py
	@echo -n 'vcard            ' && python2.7 mailpile/vcard.py
	@echo -n 'workers          ' && python2.7 mailpile/workers.py
	@echo -n 'packing          ' && python2.7 mailpile/packing.py
	@echo -n 'mailboxes/pop3   ' && python2.7 mailpile/mailboxes/pop3.py
	@echo -n 'mail_source/imap ' && python2.7 mailpile/mail_source/imap.py
	@echo -n 'crypto/aes_utils ' && python2.7 mailpile/crypto/aes_utils.py
	@echo 'spambayes...        ' && python2.7 mailpile/spambayes/Tester.py
	@echo 'crypto/streamer...'   && python2.7 mailpile/crypto/streamer.py
	@echo

clean:
	@rm -f `find . -name \\*.pyc` \
	       `find . -name \\*.pyo` \
	       `find . -name \\*.mo` \
	        mailpile-tmp.py mailpile.py \
	        ChangeLog AUTHORS \
	        .appver MANIFEST .SELF .*deps \
                dist/*.tar.gz dist/*.deb dist/*.rpm \
	        scripts/less-compiler.mk ghostdriver.log
	@rm -rf *.egg-info build/ \
               mailpile/tests/data/tmp/ testing/tmp/
	@rm -f shared-data/multipile/www/admin.cgi

mrproper: clean
	@rm -rf shared-data/locale/?? shared-data/locale/??[_@]*
	@rm -rf bower_components/ shared-data/locale/mailpile.pot
	@rm -rf mp-virtualenv/
	git reset --hard && git clean -dfx

sdist: clean
	@python setup.py sdist

#bdist-prep: compilemessages web -- FIXME: Make building web assets work!
bdist-prep: compilemessages
	@true

bdist:
	@python setup.py bdist_wheel

virtualenv: mp-virtualenv/bin/activate
virtualenv-dev: mp-virtualenv/bin/.dev

mp-virtualenv/bin/activate:
	virtualenv -p python2.7 --system-site-packages mp-virtualenv
	bash -c 'source mp-virtualenv/bin/activate && pip install -r requirements.txt && python setup.py install'
	@rm -rf mp-virtualenv/bin/.dev
	@echo
	@echo NOTE: If you want to test/develop with GnuPG 2.1, you might
	@echo       want to activate the virtualenv and then run this script
	@echo to build GnuPG 2.1: ./scripts/add-gpgme-and-gnupg-to-venv
	@echo

mp-virtualenv/bin/.dev: virtualenv
	rm -rf mp-virtualenv/lib/python2.7/site-packages/mailpile
	cd mp-virtualenv/lib/python2.7/site-packages/ && ln -s ../../../../mailpile
	rm -rf mp-virtualenv/share/mailpile
	cd mp-virtualenv/share/ && ln -s ../../shared-data mailpile
	@touch mp-virtualenv/bin/.dev

bower_components:
	@bower install

js: bower_components
	# Warning: Horrible hack to extract rules from Gruntfile.js
	@rm -f shared-data/default-theme/js/libraries.min.js
	@rm -f shared-data/default-theme/js/mailpile-min.js.tmp*
	@cat Gruntfile.js \
                |sed -e '1,/concat:/d ' \
                |sed -e '1,/src:/d' -e '/dest:/,$$d' \
                |grep / \
                |sed -e "s/[',]/ /g" \
            |xargs sed -e '$$a;' \
            >> shared-data/default-theme/js/mailpile-min.js.tmp
	@uglify -s shared-data/default-theme/js/mailpile-min.js.tmp \
               -o shared-data/default-theme/js/mailpile-min.js.tmp2
	@sed -e "s/@MP_JSBUILD_INFO@/`./scripts/gitwhere.sh`/" \
	    < shared-data/default-theme/js/libraries.js \
	    > shared-data/default-theme/js/libraries.min.js
	@echo '/* Sources...' \
	    >> shared-data/default-theme/js/libraries.min.js
	@bower --offline --no-color list \
	    >> shared-data/default-theme/js/libraries.min.js
	@echo '*/' \
	    >> shared-data/default-theme/js/libraries.min.js
	@cat shared-data/default-theme/js/mailpile-min.js.tmp2 \
            >> shared-data/default-theme/js/libraries.min.js
	@rm -f shared-data/default-theme/js/mailpile-min.js.tmp*

less: less-compiler bower_components
	@cp -fa \
                bower_components/select2/select2.png \
                bower_components/select2/select2x2.png \
                bower_components/select2/select2-spinner.gif \
            shared-data/default-theme/css/
	@make -s -f scripts/less-compiler.mk

less-loop: less-compiler
	@echo 'Running less compiler every 15 seconds. CTRL+C quits.'
	@while [ 1 ]; do \
                make -s less; \
                sleep 15; \
        done

less-compiler:
	bower install
	@cp scripts/less-compiler.in scripts/less-compiler.mk
	@find shared-data/default-theme/less/ -name '*.less' \
                |perl -npe s'/^/\t/' \
		|perl -npe 's/$$/\\/' \
                >>scripts/less-compiler.mk
	@echo >> scripts/less-compiler.mk
	@perl -e 'print "\t\@touch .less-deps", $/' >> scripts/less-compiler.mk

genmessages:
	@scripts/make-messages.sh

compilemessages:
	@scripts/compile-messages.sh

transifex:
	tx pull -a --minimum-perc=25
	tx pull -l is,en_GB


# -----------------------------------------------------------------------------
# BUILD
# -----------------------------------------------------------------------------

dist/version.txt: mailpile/config/defaults.py scripts/version.py
	mkdir -p dist
	scripts/version.py > dist/version.txt

dist/mailpile.tar.gz: mrproper genmessages transifex dist/version.txt
	git submodule update --init --recursive
	git submodule foreach 'git reset --hard && git clean -dfx'
	mkdir -p dist
	scripts/version.py > dist/version.txt
	tar --exclude='./packages/debian' --exclude=dist --exclude-vcs -czf dist/mailpile-$$(cat dist/version.txt).tar.gz -C $(shell pwd) .
	(cd dist; ln -fs mailpile-$$(cat version.txt).tar.gz mailpile.tar.gz)

.dockerignore: dist/version.txt packages/Dockerfile_debian packages/debian packages/debian/rules
	mkdir -p dist
	docker build \
	    --file=packages/Dockerfile_debian \
	    --tag=mailpile-deb-builder \
	    ./
	touch .dockerignore

dpkg: dist/mailpile.tar.gz .dockerignore
	docker run \
	    --rm --volume=$$(pwd)/dist:/mnt/dist \
	    mailpile-deb-builder


================================================
FILE: README.md
================================================
# Welcome to Mailpile! #

**IMPORTANT NOTE**

Development on this codebase has halted, until the
[Python3 rewrite](https://community.mailpile.is/t/a-very-uninformative-progress-update-mailpile-2/785)
has completed.

Apologies to those who have unanswered, out-standing pull requests and
issues. 😢 Your efforts are appreciated!

If you rely on this code and have your own branch which you actively
maintain, let us know: we would be happy to link to it.

If you need to run Mailpile v1 to access legacy data, consider using
our [legacy Docker images](https://github.com/mailpile/Mailpile-v1-Docker).


------------------------------------------------------------------------

## Introduction (Obsolete) ##

Mailpile (<https://www.mailpile.is/>) is a modern, fast web-mail client
with user-friendly encryption and privacy features. The development of
Mailpile is funded by
[a large community of backers](https://www.mailpile.is/#community)
and all code related to the project is and will be released under an OSI
approved Free Software license.

Mailpile places great emphasis on providing a clean, elegant user
interface and pleasant user experience. In particular, Mailpile aims to
make it easy and convenient to receive and send PGP encrypted or signed
e-mail.

Mailpile's primary user interface is web-based, but it also has a basic
command-line interface and an API for developers. Using web technology
for the interface allows Mailpile to function both as a local desktop
application (accessed by visiting `localhost` in the browser) or a
remote web-mail on a personal server or VPS.

The core of Mailpile is a fast search engine, custom written to deal
with large volumes of e-mail on consumer hardware. The search engine
allows e-mail to be organized using tags (similar to GMail's labels) and
the application can be configured to automatically tag incoming mail
either based on static rules or bayesian classifiers.


### Trying Mailpile

If you need to run Mailpile v1 to access legacy data, consider using
our [legacy Docker images](https://github.com/mailpile/Mailpile-v1-Docker).


## Credits and License ##

Bjarni R. Einarsson (<http://bre.klaki.net/>) created this!  If you
think it's neat, you should also check out PageKite:
<https://pagekite.net/>. [Smári](<http://www.smarimccarthy.is/>) and
[Brennan](https://brennannovak.com) joined the team in 2013 and made
this a real project (not just a toy search engine).

The original GMail team deserve a mention for their inspiring work:
wishing the Free Software world had something like GMail is what
motivated Bjarni to start working on Mailpile. We would also like to
thank Edward Snowden for inspiring us to try and make PGP usable for
journalists and everday folks!

Contributors:

- Bjarni R. Einarsson (<http://bre.klaki.net/>)
- Brennan Novak (<https://brennannovak.com/>)
- Smari McCarthy (<http://www.smarimccarthy.is/>)
- Lots more, run `git shortlog -s` for a list! (Or check
  [GitHub](https://github.com/mailpile/Mailpile/graphs/contributors).)

And of course, we couldn't do this without [our community of
backers](https://www.mailpile.is/#community).

This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published by
the Free Software Foundation. See the file `COPYING.md` for details.



================================================
FILE: babel.cfg
================================================
# Extraction from Python source files
[python: mailpile/**.py]
encoding = utf-8

[python: shared-data/contrib/**.py]
encoding = utf-8

[python: shared-data/multiple/**.py]
encoding = utf-8

[python: shared-data/mailpile-gui/**.py]
encoding = utf-8

# Extraction from Jinja2 template files
[jinja2: shared-data/default-theme/html/**]
encoding = utf-8
ignore_tags = style
extensions = jinja2.ext.do,jinja2.ext.autoescape,mailpile.www.jinjaextensions.MailpileCommand
silent = false

[jinja2: shared-data/contrib/html/**.html]
encoding = utf-8
ignore_tags = style
extensions = jinja2.ext.do,jinja2.ext.autoescape,mailpile.www.jinjaextensions.MailpileCommand
silent = false

[jinja2: shared-data/contrib/html/**.js]
encoding = utf-8
ignore_tags = style
extensions = jinja2.ext.do,jinja2.ext.autoescape,mailpile.www.jinjaextensions.MailpileCommand
silent = false


================================================
FILE: bower.json
================================================
{
  "name": "mailpile",
  "version": "0.1.0",
  "homepage": "https://mailpile.is",
  "authors": [
    "Various"
  ],
  "description": "Front end libraries and dependencies for Mailpile default theme",
  "main": "libraries.js",
  "license": "Various",
  "private": true,
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "underscore": ">=1.7.0",
    "bootstrap": "~3.3.0",
    "favico.js": "~0.3.5",
    "html5shiv": "~3.7.0",
    "jquery": "~2.1",
    "autosize": "~3.0.14",
    "jquery-confirm": "~2.1.1",
    "jquery-slugify": "~1.0.3",
    "jquery-timer": "*",
    "jquery.ui": "~1.10",
    "less-elements-old": "~1.0",
    "mousetrap": "~1.6.0",
    "moxie": "~1.3.4",
    "plupload": "~2.1.2",
    "qtip2": "~2.1.1",
    "rebar": "~0.3.2",
    "select2": "3.5.4",
    "typeahead.js": "~0.10.5",
    "jqueryui-touch-punch": "*",
    "dompurify": "~1.0.11"
  }
}


================================================
FILE: docker-compose.dev.yml
================================================
version: '3.0'
services:
  mailpile_dev:
    tty: true
    stdin_open: true
    container_name: mailpile_dev
    build:
      context: .
      dockerfile: Dockerfile.dev
    image: mailpile_dev
    volumes:
      - .:/Mailpile
      - .dev-mailpile-data:/mailpile-data:rw
    ports:
      - 33412:33411



================================================
FILE: docker-compose.yml
================================================
version: '3.0'
services:
  mailpile:
    container_name: mailpile
    build: .
    image: mailpile
    volumes:
      - .:/Mailpile
      - .dev-mailpile-data:/mailpile-data
    ports:
      - 33411:33411



================================================
FILE: install_hooks.py
================================================
import os
import sys

def symlink_develop(config):
    if 'develop' in sys.argv:
        share_path = os.path.join(sys.prefix, 'share')

        if not os.path.exists(share_path):
            os.makedirs(share_path)

        os.symlink(
            os.path.join(
                os.path.dirname(os.path.realpath(__file__)),
                'shared-data'
            ),
            os.path.join(sys.prefix, 'share', 'mailpile')
        )


================================================
FILE: mailpile/__init__.py
================================================
from mailpile.i18n import gettext as _
from mailpile.i18n import ngettext as _n


__all__ = ['Mailpile',
           "app", "commands", "plugins", "mailutils", "search", "ui", "util"]


class Mailpile(object):
    """This object provides a simple Python API to Mailpile."""

    def __init__(self,
                 ui=None,
                 workdir=None,
                 session=None):
        import mailpile.app
        import mailpile.config.defaults
        import mailpile.ui
        if not session:
            ui = ui or mailpile.ui.UserInteraction
            self._config = mailpile.app.ConfigManager(
                workdir=workdir, rules=mailpile.config.defaults.CONFIG_RULES)
            self._session = mailpile.ui.Session(self._config)
            self._ui = self._session.ui = ui(self._config)
            self._session.config.load(self._session)
            self._session.main = True
        else:
            self._session = session
            self._config = session.config
            self._ui = session.ui

        for cls in mailpile.commands.COMMANDS:
            names, argspec = cls.SYNOPSIS[1:3], cls.SYNOPSIS[3]
            if names[0]:
                setattr(self, *self._mk_action(cls, names[0], argspec))
            if names[1] and (names[0] != names[1]):
                setattr(self, *self._mk_action(cls, names[1], argspec))

    def _mk_action(self, cls, cmd, argspec):
        import mailpile.commands
        if argspec:

            def fnc(*args, **kwargs):
                return mailpile.commands.Action(self._session, cmd, args,
                                                data=kwargs)
        else:

            def fnc(**kwargs):
                return mailpile.commands.Action(self._session, cmd, '',
                                                data=kwargs)

        fnc.__doc__ = '%s(%s)  # %s' % (cmd, argspec or '', cls.__doc__)
        return cmd.replace('/', '_'), fnc

    def Interact(self):
        import mailpile.util
        mailpile.util.QUITTING = False
        self._session.interactive = self._session.ui.interactive = True
        try:
            self._session.config.prepare_workers(self._session, daemons=True)
            mailpile.app.Interact(self._session)
        except KeyboardInterrupt:
            pass
        finally:
            mailpile.util.QUITTING = mailpile.util.QUITTING or True
            self._session.config.stop_workers()
            self._session.interactive = self._session.ui.interactive = False


================================================
FILE: mailpile/__main__.py
================================================
import sys
from mailpile.app import Main


def main():
    Main(sys.argv[1:])


if __name__ == "__main__":
    main()


================================================
FILE: mailpile/app.py
================================================
from __future__ import print_function
import getopt
import gettext
import locale
import os
import re
import sys
import traceback

import mailpile.util
import mailpile.config.defaults
import mailpile.platforms
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.plugins.setup_magic import Setup
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 threaded_raw_input(prompt):
    """These shenigans are necessary to let Quit work reliably."""
    def reader(container):
        try:
            line = raw_input(prompt).decode('utf-8').strip()
            container.append(line)
        except EOFError:
            pass
    o = []
    t = threading.Thread(target=reader, args=(o,))
    t.daemon = True
    t.start()
    while t.isAlive() and not mailpile.util.QUITTING:
        t.join(timeout=1)
    if not o:
        raise EOFError()
    return o[0]


def write_readline_history(session):
    try:
        if session.config.sys.history_length > 0:
            readline.write_history_file(session.config.history_file())
        else:
            safe_remove(session.config.history_file())
    except (OSError, AttributeError, IOError):
        pass


def CatchUnixSignals(session):
    def quit_app(sig, stack):
        Quit(session, 'quit').run()

    def reload_app(sig, stack):
        pass

    try:
        import signal
        if os.name != 'nt':
            signal.signal(signal.SIGTERM, quit_app)
            signal.signal(signal.SIGQUIT, quit_app)
            signal.signal(signal.SIGUSR1, reload_app)
        else:
            signal.signal(signal.SIGTERM, quit_app)
    except ImportError:
        pass


def FriendlyPipeTransform(session, opt):
    """Shell-like syntax to invoke the pipe command or change render modes."""
    old_render_mode = None
    if session.config.prefs.friendly_pipes:
        if ' |' in opt:
            cmd, pipe = opt.split(' |', 1)
            opt = 'pipe %s -- %s' % (pipe.strip(), cmd.strip())
        elif re.search(' >\S+$', opt):
            cmd, pipe = opt.rsplit(' >', 1)
            opt = 'pipe >%s -- %s' % (pipe, cmd.strip())
            opt = opt.replace('\\|', '|').replace('\\>', '>')

        if re.search(' :(json|j?html|text|\S+.html(?:!\S+))$', opt):
            old_render_mode = session.ui.render_mode
            opt, session.ui.render_mode = opt.rsplit(' :', 1)

    return old_render_mode, opt


def Interact(session):
    global readline
    try:
        import readline as rl  # Unix-only
        readline = rl
    except ImportError:
        pass

    try:
        if readline:
            readline.read_history_file(session.config.history_file())
            readline.set_completer_delims(Completer.DELIMS)
            readline.set_completer(Completer(session).get_completer())
            for opt in ["tab: complete", "set show-all-if-ambiguous on"]:
                readline.parse_and_bind(opt)
    except IOError:
        pass

    # Negative history means no saving state to disk.
    history_length = session.config.sys.history_length
    if readline is None:
        pass  # history currently not supported under Windows / Mac
    elif history_length >= 0:
        readline.set_history_length(history_length)
    else:
        readline.set_history_length(-history_length)

    try:
        prompt = session.ui.term.color('mailpile> ',
                                       color=session.ui.term.BLACK,
                                       weight=session.ui.term.BOLD,
                                       readline=(readline is not None))
        while not mailpile.util.QUITTING:
            try:
                with session.ui.term:
                    if Setup.Next(session.config, 'anything') != 'anything':
                        session.ui.notify(
                            _('Mailpile is unconfigured, please run `setup`'
                              ' or visit the web UI.'))
                    session.ui.block()
                    opt = threaded_raw_input(prompt)
            except KeyboardInterrupt:
                session.ui.unblock(force=True)
                session.ui.notify(_('Interrupted. '
                                    'Press CTRL-D or type `quit` to quit.'))
                continue
            session.ui.term.check_max_width()
            session.ui.unblock(force=True)
            if opt:
                old_render_mode, opt = FriendlyPipeTransform(session, opt)
                if ' ' in opt:
                    opt, arg = opt.split(' ', 1)
                else:
                    arg = ''
                try:
                    result = Action(session, opt, arg)
                    session.ui.block()
                    session.ui.display_result(result)
                except UsageError as e:
                    session.fatal_error(unicode(e))
                except UrlRedirectException as e:
                    session.fatal_error('Tried to redirect to: %s' % e.url)
                if old_render_mode is not None:
                    session.ui.render_mode = old_render_mode
    except EOFError:
        print()
    finally:
        session.ui.unblock(force=True)

    write_readline_history(session)


class InteractCommand(Command):
    SYNOPSIS = (None, 'interact', None, None)
    ORDER = ('Internals', 2)
    CONFIG_REQUIRED = False
    RAISES = (KeyboardInterrupt,)

    def command(self):
        session, config = self.session, self.session.config

        session.interactive = True
        if mailpile.platforms.TerminalSupportsAnsiColors():
            session.ui.term = ANSIColors()

        # Ensure we have a working GnuPG
        self._gnupg().common_args(will_send_passphrase=True)

        # Create and start the rest of the threads, load the index.
        if config.loaded_config:
            Load(session, '').run(quiet=True)
        else:
            config.prepare_workers(session, daemons=True)

        # Note: We do *not* update the MOTD on startup, to keep things
        #       fast, and to avoid leaking our IP on setup, before Tor
        #       has been configured.
        splash = HelpSplash(session, 'help', []).run()
        motd = MessageOfTheDay(session, 'motd', ['--noupdate']).run()
        session.ui.display_result(splash)
        print()  # FIXME: This is a hack!
        session.ui.display_result(motd)

        Interact(session)

        return self._success(_('Ran interactive shell'))


class WaitCommand(Command):
    SYNOPSIS = (None, 'wait', None, None)
    ORDER = ('Internals', 2)
    CONFIG_REQUIRED = False
    RAISES = (KeyboardInterrupt,)

    def command(self):
        self.session.ui.display_result(HelpSplash(self.session, 'help', []
                                                  ).run(interactive=False))
        while not mailpile.util.QUITTING:
            time.sleep(1)
        return self._success(_('Did nothing much for a while'))


def Main(args):
    try:
        mailpile.platforms.DetectBinaries(_raise=OSError)
    except OSError as e:
        binary = str(e).split()[0]
        sys.stderr.write("""
Required binary missing or unusable: %s

If you know where it is, or would like to skip this test and run Mailpile
anyway, you can set one of the following environment variables:

    MAILPILE_%s="/path/to/binary"
or
    MAILPILE_IGNORE_BINARIES="%s"  # Can be a space-separated list

Note that skipping a binary check may cause the app to become unstable or
fail in unexpected ways. If it breaks you get to keep both pieces!

""" % (e, binary.upper(), binary))
        sys.exit(1)

    # Enable our connection broker, try to prevent badly behaved plugins from
    # bypassing it.
    DisableUnbrokeredConnections()

    # Bootstrap translations until we've loaded everything else
    mailpile.i18n.ActivateTranslation(None, ConfigManager, None)
    try:
        # Create our global config manager and the default (CLI) session
        config = ConfigManager(rules=mailpile.config.defaults.CONFIG_RULES)
        session = Session(config)
        cli_ui = session.ui = UserInteraction(config)
        session.main = True
        try:
            CatchUnixSignals(session)
            config.clean_tempfile_dir()
            config.load(session)
        except IOError:
            if config.sys.debug:
                session.ui.error(_('Failed to decrypt configuration, '
                                   'please log in!'))
        HealthCheck(session, None, []).run()
        config.prepare_workers(session)
    except AccessError as e:
        session.ui.error('Access denied: %s\n' % e)
        sys.exit(1)

    try:
        try:
            if '--login' in args:
                a1 = args[:args.index('--login') + 1]
                a2 = args[len(a1):]
            else:
                a1, a2 = args, []

            allopts = []
            for argset in (a1, a2):
                shorta, longa = '', []
                for cls in COMMANDS:
                    shortn, longn, urlpath, arglist = cls.SYNOPSIS[:4]
                    if arglist:
                        if shortn:
                            shortn += ':'
                        if longn:
                            longn += '='
                    if shortn:
                        shorta += shortn
                    if longn:
                        longa.append(longn.replace(' ', '_'))

                opts, args = getopt.getopt(argset, shorta, longa)
                allopts.extend(opts)
                for opt, arg in opts:
                    session.ui.display_result(Action(
                        session, opt.replace('-', ''), arg.decode('utf-8')))
                if args:
                    session.ui.display_result(Action(
                        session, args[0], ' '.join(args[1:]).decode('utf-8')))

        except (getopt.GetoptError, UsageError) as e:
            session.fatal_error(unicode(e))

        if (not allopts) and (not a1) and (not a2):
            InteractCommand(session).run()

    except KeyboardInterrupt:
        pass

    except:
        traceback.print_exc()

    finally:
        write_readline_history(session)

        # Make everything in the background quit ASAP...
        mailpile.util.LAST_USER_ACTIVITY = 0
        mailpile.util.QUITTING = mailpile.util.QUITTING or True

        if config.plugins:
            config.plugins.process_shutdown_hooks()

        config.stop_workers()
        if config.index:
            config.index.save_changes()
        if config.event_log:
            config.event_log.close()

        session.ui.display_result(Action(session, 'cleanup', ''))

        if session.interactive and config.sys.debug:
            session.ui.display_result(Action(session, 'ps', ''))

        # Remove anything that we couldn't remove before
        safe_remove()

        # Restart the app if that's what was requested
        if mailpile.util.QUITTING == 'restart':
            os.execv(sys.argv[0], sys.argv)


_plugins.register_commands(InteractCommand, WaitCommand)

if __name__ == "__main__":
    Main(sys.argv[1:])


================================================
FILE: mailpile/auth.py
================================================
import time
from urlparse import parse_qs, urlparse
from urllib import quote, urlencode

from mailpile.commands import Command
from mailpile.crypto.gpgi import GnuPG
from mailpile.i18n import gettext as _
from mailpile.i18n import ngettext as _n
from mailpile.plugins import PluginManager
from mailpile.security import SecurePassphraseStorage
from mailpile.util import *

GLOBAL_LOGIN_LOCK = CryptoLock()


class UserSession(object):
    EXPIRE_AFTER = 7 * 24 * 3600

    def __init__(self, ts=None, auth=None, data=None):
        self.ts = ts or time.time()
        self.auth = auth
        self.data = data or {}

    def is_expired(self, now=None):
        return (self.ts < (now or time.time()) - self.EXPIRE_AFTER)

    def update_ts(self):
        self.ts = time.time()


class UserSessionCache(dict):
    def delete_expired(self, now=None):
        now = now or time.time()
        for k in self.keys():
            if self[k].is_expired(now=now):
                del self[k]


def VerifyAndStorePassphrase(config, passphrase=None, sps=None,
                                     key=None):
    if passphrase and not sps:
        sps = SecurePassphraseStorage(passphrase)
        passphrase = 'this probably does not really overwrite :-( '

    safe_assert(
        (sps is not None) and
        (config.load_master_key(sps)))

    # Fun side effect: changing the passphrase invalidates the message cache
    import mailpile.mailutils.emails
    mailpile.mailutils.emails.ClearParseCache(full=True)

    return sps


def SetLoggedIn(cmd, user=None, redirect=False, session_id=None):
    user = user or 'DEFAULT'

    sid = session_id or cmd.session.ui.html_variables.get('http_session')
    if sid:
        if cmd:
            cmd.session.ui.debug('Logged in %s as %s' % (sid, user))
        SESSION_CACHE[sid] = UserSession(auth=user, data={
            't': '%x' % int(time.time()),
        })

    if cmd:
        if redirect:
            return cmd._do_redirect()
    else:
        return True


def CheckPassword(config, username, password):
    # FIXME: Do something with the username
    username = username or 'DEFAULT'
    sps = config.passphrases and config.passphrases.get(username)
    return sps.compare(password) and username


def IndirectPassword(config, pwd):
    pp = pwd.split(':')
    if len(pp) > 2 and pp[0] == '_SECRET_':
        if pp[1] in config.secrets:
            return config.secrets[pp[1]].password
        if pp[1] in config.passphrases:
            return config.passphrases[pp[1]].get_passphrase()
    return pwd


SESSION_CACHE = UserSessionCache()
LOGIN_FAILURES = []


def LogoutAll():
    for k in list(SESSION_CACHE.keys()):
        del SESSION_CACHE[k]


class Authenticate(Command):
    """Authenticate a user (log in)"""
    SYNOPSIS = (None, 'login', 'auth/login', None)
    ORDER = ('Internals', 5)
    SPLIT_ARG = False
    IS_INTERACTIVE = True

    CONFIG_REQUIRED = False
    HTTP_AUTH_REQUIRED = False
    HTTP_STRICT_VARS = False
    HTTP_CALLABLE = ('GET', 'POST')
    HTTP_POST_VARS = {
        'user': 'User to authenticate as',
        'pass': 'Password or passphrase'
    }

    @classmethod
    def RedirectBack(cls, url, data):
        qs = [(k, v.encode('utf-8')) for k, vl in data.iteritems() for v in vl
              if k not in ['_method', '_path'] + cls.HTTP_POST_VARS.keys()]
        qs = urlencode(qs)
        url = ''.join([url, '?%s' % qs if qs else ''])
        raise UrlRedirectException(url)

    def _result(self, result=None):
        global LOGIN_FAILURES
        result = result or {}
        result['login_banner'] = self.session.config.sys.login_banner
        result['login_failures'] = LOGIN_FAILURES
        return result

    def _error(self, message, info=None, result=None):
        global LOGIN_FAILURES
        LOGIN_FAILURES.append(int(time.time()))
        time.sleep(min(5, 0.1 + len(LOGIN_FAILURES) / 2))
        return Command._error(self, message,
                              info=info, result=self._result(result))

    def _success(self, message, result=None):
        return Command._success(self, message, result=self._result(result))

    def _do_redirect(self):
        path = self.data.get('_path', [None])[0]

        # These are here to prevent people from abusing this to redirect to
        # arbitrary URLs on the Internet.
        if path:
            url = urlparse(path)
            safe_assert(not url.scheme and not url.netloc)

        if (path and
               not path[1:].startswith(DeAuthenticate.SYNOPSIS[2] or '!') and
               not path[1:].startswith(self.SYNOPSIS[2] or '!')):
            self.RedirectBack(self.session.config.sys.http_path + path, self.data)
        else:
            raise UrlRedirectException('%s/' % self.session.config.sys.http_path)

    def _do_login(self, user, password, load_index=False, redirect=False):
        global LOGIN_FAILURES
        session, config = self.session, self.session.config
        session_id = self.session.ui.html_variables.get('http_session')

        # This prevents folks from sending us a DEFAULT user (upper case),
        # which is an internal security bypass below.
        user = user and user.lower()

        if not user:
            try:
                # Verify the passphrase
                if CheckPassword(config, None, password):
                    sps = config.passphrases['DEFAULT']
                else:
                    sps = VerifyAndStorePassphrase(config, passphrase=password)

                if sps:
                    # Load the config and index, if necessary
                    config = self._config()
                    self._idx(wait=False)
                    if load_index:
                        try:
                            while not config.index:
                                time.sleep(1)
                        except KeyboardInterrupt:
                            pass

                    session.ui.debug('Good passphrase for %s' % session_id)
                    self.record_user_activity()
                    LOGIN_FAILURES = []
                    return self._success(_('Hello world, welcome!'), result={
                        'authenticated': SetLoggedIn(self, redirect=redirect)
                    })
                else:
                    session.ui.debug('No GnuPG, checking DEFAULT user')
                    # No GnuPG, see if there is a DEFAULT user in the config
                    user = 'DEFAULT'

            except (AssertionError, IOError):
                session.ui.debug('Bad passphrase for %s' % session_id)
                return self._error(_('Invalid password, please try again'))

        if user in config.logins or user == 'DEFAULT':
            # FIXME: Salt and hash the password, check if it matches
            #        the entry in our user/password list (TODO).
            # NOTE:  This hack effectively disables auth without GnUPG
            if user == 'DEFAULT':
                session.ui.debug('FIXME: Unauthorized login allowed')
                return self._logged_in(redirect=redirect)
            raise Exception('FIXME')

        return self._error(_('Incorrect username or password'))

    def command(self):
        session_id = self.session.ui.html_variables.get('http_session')

        if self.data.get('_method', '') == 'POST':
            if 'pass' in self.data:
                with GLOBAL_LOGIN_LOCK:
                    return self._do_login(self.data.get('user', [None])[0],
                                          self.data['pass'][0],
                                          redirect=True)

        elif not self.data:
            password = self.session.ui.get_password(_('Your password: '))
            return self._do_login(None, password, load_index=True)

        elif (session_id in SESSION_CACHE and
                SESSION_CACHE[session_id].auth and
                '_method' in self.data):
            self._do_redirect()

        return self._success(_('Please log in'))


class DeAuthenticate(Command):
    """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!'))


class SetPassphrase(Command):
    """Manage storage of passwords (passphrases)"""
    SYNOPSIS = (None, 'set/password', 'settings/set/password',
                      '<keyid> [store|cache-only[:<ttl>]|fail|forget]')
    ORDER = ('Config', 9)
    SPLIT_ARG = True
    IS_INTERACTIVE = True
    IS_USER_ACTIVITY = True
    CONFIG_REQUIRED = True
    HTTP_AUTH_REQUIRED = True
    HTTP_CALLABLE = ('GET', 'POST')
    HTTP_QUERY_VARS = {
        'id': 'KeyID or account name',
        'mailsource': 'Mail source ID',
        'mailroute': 'Mail route ID',
        'is_locked': 'Assume key is locked'}
    HTTP_POST_VARS = {
        'password': 'KeyID or account name',
        'policy-ttl': 'Combined policy and TTL',
        'policy': 'store|cache-only|fail|forget',
        'ttl': 'Seconds after which it expires, -1 = never',
        'update_mailsources': 'If true, update mail source settings',
        'update_mailroutes': 'If true, update mail route settings',
        'redirect': 'URL to redirect to on success'}

    def _get_profiles(self):
        return self.session.config.vcards.find_vcards([], kinds=['profile'])

    def _get_policy(self, fingerprint):
        if fingerprint in self.session.config.secrets:
            return self.session.config.secrets[fingerprint].policy
        elif fingerprint in self.session.config.passphrases:
            return 'cache-only'
        return None

    def _massage_key_info(self, fingerprint, key_info, profiles=None, is_locked=False):
        config = self.session.config
        fingerprint = fingerprint.lower()

        key_info["uids"].sort(
            key=lambda k: (k.get("name"), k.get("email"), k.get("comment")))

        if not is_locked:
            key_info['policy'] = self._get_policy(fingerprint)
            if key_info['policy'] is None:
                del key_info['policy']

        key_info["accounts"] = []
        if profiles is None:
            profiles = self._get_profiles()
        for vc in profiles:
            vc_pgp_key = (vc.pgp_key or '').lower()
            if vc_pgp_key == fingerprint:
                key_info["accounts"].append({
                    'name': vc.fn,
                    'email': vc.email,
                    'rid': vc.random_uid})

        return key_info

    def _lookup_key(self, keyid, **kwargs):
        keylist = self._gnupg().list_secret_keys(selectors=[keyid])
        if len(keylist) != 1:
            raise ValueError("Too many or too few keys found!")
        fingerprint, key_info = keylist.keys()[0], keylist.values()[0]
        return fingerprint, self._massage_key_info(
            fingerprint, key_info, **kwargs)

    def _list_keys(self, **kwargs):
        keylist = self._gnupg().list_secret_keys()
        profiles = self._get_profiles()
        for fingerprint, key_info in keylist.iteritems():
            self._massage_key_info(fingerprint, key_info,
                                   profiles=profiles, **kwargs)
        return keylist

    def _get_account(self, cfg):
        username = cfg.username
        if cfg.username and '@' not in cfg.username:
            username = '%s@%s' % (username, cfg.host)
        return username

    def _user_fingerprint(self, username):
        return username.replace('@', '_').replace('.', '_').lower()

    def _list_accounts(self, only=None):
        accounts = {}
        def _add_account(cfg, which):
            username = self._get_account(cfg)
            if (username
                    and ((username == only) or only is None)
                    and cfg.auth_type == 'password'):
                if username in accounts:
                    accounts[username][which] = cfg.host
                else:
                    fingerprint = self._user_fingerprint(username)
                    accounts[username] = {
                        which: cfg.host,
                        'username': username,
                        'policy': self._get_policy(fingerprint)}
                    if accounts[username]['policy'] is None:
                        del accounts[username]['policy']

        for msid, route in self.session.config.routes.iteritems():
            _add_account(route, 'route')
        for msid, source in self.session.config.sources.iteritems():
            _add_account(source, 'source')

        return accounts

    def _account_details(self, account):
        return self._list_accounts(only=account).get(account)

    def _check_master_password(self, password, account=None, fingerprint=None):
        return CheckPassword(self.session.config, None, password)

    def _check_password(self, password, account=None, fingerprint=None):
        if account:
            # We're going to keep punting on this for a while...
            return True
        elif fingerprint:
            sps = SecurePassphraseStorage(password)
            gpg = GnuPG(self.session.config)
            status, sig = gpg.sign('OK', fromkey=fingerprint, passphrase=sps)
            return (status == 0)
        else:
            return True

    def _prepare_result(self, account=None, keyid=None, is_locked=False):
        if account:
            fingerprint = self._user_fingerprint(account)
            result = {'account': self._account_details(account)}
        elif keyid:
            fingerprint, info = self._lookup_key(keyid, is_locked=is_locked)
            result = {'key': info}
        else:
            fingerprint = None
            result = {
                'keylist': self._list_keys(is_locked=is_locked),
                'accounts': self._list_accounts()}
        return fingerprint, result

    def command(self):
        config = self.session.config

        policyttl = self.args[1] if (len(self.args) > 1) else 'cache-only:-1'
        is_locked = self.data.get('is_locked', [0])[0]
        if 'policy-ttl' in self.data:
            policyttl = self.data['policy-ttl'][0]
        if ':' in policyttl:
            policy, ttl = policyttl.split(':')
        else:
            policy, ttl = policyttl, -1
        if 'policy' in self.data:
            policy = self.data['policy'][0]
        if 'ttl' in self.data:
            ttl = self.data['policy'][0]
        ttl = float(ttl)

        fingerprint = info = account = keyid = None
        which = self.args[0] if self.args else self.data.get('id', [None])[0]
        if which and '@' in which:
            account = which
        else:
            keyid = which

        if not account and not keyid:
            msid = self.data.get('mailsource', [None])[0]
            if msid:
                account = self._get_account(config.sources[msid])
            mrid = self.data.get('mailroute', [None])[0]
            if mrid:
                account = self._get_account(config.routes[mrid])

        fingerprint, result = self._prepare_result(
            account=account, keyid=keyid, is_locked=is_locked)

        if policy in ('display', 'unprotect'):
            pass_prompt = _('Enter your Mailpile password')
            pass_check = self._check_master_password
        else:
            pass_prompt = _('Enter your password')
            pass_check = self._check_password

        if self.data.get('_method', None) == 'GET':
            return self._success(pass_prompt, result)

        safe_assert(fingerprint is not None)
        fingerprint = fingerprint.lower()
        if fingerprint in config.secrets:
            if config.secrets[fingerprint].policy == 'protect':
                if policy not in ('unprotect', 'display'):
                    result['error'] = _('That key is managed by Mailpile,'
                                        ' it cannot be changed directly.')
                    return self._error(_('Protected secret'), result=result)

        if self.data.get('_method', None) == 'POST':
            password = self.data.get('password', [None])[0]
            update_ms = self.data.get('update_mailsources', [False])[0]
            update_mr = self.data.get('update_mailroutes', [False])[0]
        else:
            password = self.session.ui.get_password(pass_prompt + ': ')
            update_ms = update_mr = (account is not None)

        if update_ms or update_mr:
            safe_assert(account is not None)

        if not pass_check(password, account=account, fingerprint=fingerprint):
            result['error'] = _('Password incorrect! Try again?')
            return self._error(_('Incorrect password'), result=result)

        def _account_matches(cfg):
            return (account == cfg.username or
                    account == '%s@%s' % (cfg.username, cfg.host))
        def happy(msg, refresh=True, changed=True):
            if changed:
                # Fun side effect: changing the passphrase invalidates the
                # message cache
                import mailpile.mailutils.emails
                mailpile.mailutils.emails.ClearParseCache(full=True)

                indirect_pwd = '_SECRET_:%s:%s' % (fingerprint, time.time())
                if update_ms:
                    for msid, source in config.sources.iteritems():
                        if _account_matches(source):
                            source.password = indirect_pwd
                if update_mr:
                    for msid, route in config.routes.iteritems():
                        if _account_matches(route):
                            route.password = indirect_pwd

                self._background_save(config=True)

            redirect = self.data.get('redirect', [None])[0]
            if redirect:
                raise UrlRedirectException(redirect)

            result['op_completed'] = policy
            if refresh:
              fp, r = self._prepare_result(account=account, keyid=keyid)
              result.update(r)

            return self._success(msg, result)

        if policy == 'display':
            if fingerprint in config.passphrases:
                pwd = config.passphrases[fingerprint].get_passphrase()
            elif fingerprint in config.secrets:
                pwd = config.secrets[fingerprint].password
            else:
                return self._error(_('No password found'), result=result)
            result['stored_password'] = pwd
            return happy(_('Retrieved stored password'),
                         refresh=False, changed=False)

        if policy == 'forget':
            if fingerprint in config.passphrases:
                del config.passphrases[fingerprint]
            if fingerprint in config.secrets:
                config.secrets[fingerprint] = {'policy': 'fail'}
                del config.secrets[fingerprint]
            return happy(_('Password forgotten!'))

        if policy == 'fail':
            if fingerprint in config.passphrases:
                del config.passphrases[fingerprint]
            config.secrets[fingerprint] = {'policy': policy}
            return happy(_('Password will never be stored'))

        if policy == 'store':
            if fingerprint in config.passphrases:
                del config.passphrases[fingerprint]
            config.secrets[fingerprint] = {
                'password': password,
                'policy': policy}
            return happy(_('Password remembered!'))

        elif policy == 'cache-only' and password:
            sps = SecurePassphraseStorage(password)
            if ttl > 0:
                sps.expiration = time.time() + ttl
            config.passphrases[fingerprint] = sps
            if fingerprint in config.secrets:
                config.secrets[fingerprint] = {'policy': 'fail'}
                del config.secrets[fingerprint]
            return happy(_('The password has been stored temporarily'))

        else:
            return self._error(_('Invalid password policy'), result=result)


plugin_manager = PluginManager(builtin=True)
plugin_manager.register_commands(Authenticate,
                                 DeAuthenticate,
                                 SetPassphrase)


================================================
FILE: mailpile/command_cache.py
================================================
import time

import mailpile.util
from mailpile.commands import Command
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.util import *
from mailpile.ui import Session, BackgroundInteraction


_plugins = PluginManager(builtin=__name__)


class CommandCache(object):
    #
    # This is an in-memory cache of commands and results we may want to
    # refresh in the background and/or reuse.
    #
    # The way this works:
    #     - Cache-able commands generate a fingerprint describing themselves.
    #     - If the fingerprint is found in cache, reuse the result object.
    #     - Otherwise, run, generate a list of requirements and cache all of:
    #       the command object itself, the requirements, the result object.
    #     - Internal state changes (tag ops, new mail, etc.) call mark_dirty()
    #       describing which assets (requirements) have changed.
    #     - Periodically, the cache is refreshed, which re-runs any dirtied
    #       commands and fires events notifying the UI about changes.
    #
    # Examples of requirements:
    #
    #    - Search terms, eg. 'in:inbox' or 'potato' or 'salad'
    #    - Messages: 'msg:INDEX' where INDEX is a number (not a MID)
    #    - Threads: 'thread:MID' were MID is the thread ID.
    #    - The app configuration: '!config'
    #

    def __init__(self, debug=None):
        self.debug = debug or (lambda s: None)
        self.lock = UiRLock()
        self._lag = 0.1
        self.cache = {}       # id -> [exp, req, ss, cmd_obj, res_obj, added]
        self.dirty = []       # (ts, req): Requirements that changed & when
        self._dirty_ttl = 10

    def cache_result(self, fprint, expires, req, cmd_obj, result_obj):
        with self.lock:
            # Make a snapshot of the session, as it provides context
            ui = BackgroundInteraction(cmd_obj.session.config,
                                       log_parent=cmd_obj.session.ui)
            ss = Session.Snapshot(cmd_obj.session, ui=ui)

            # Note: We cache this even if the requirements are "dirty",
            #       as mere presence in the cache makes this a candidate
            #       for refreshing.
            self.cache[str(fprint)] = [expires, req, ss, cmd_obj, result_obj,
                                       time.time()]
            self.debug('Cached %s, req=%s' % (fprint, sorted(list(req))))

    def get_result(self, fprint, dirty_check=True, extend=300):
        with self.lock:
            exp, req, ss, co, result_obj, a = match = self.cache[fprint]
        if dirty_check:
            recent = (a > time.time() - self._lag)
            dirty = (req & self.dirty_set(after=a))
            if recent or dirty:
                # If item is too new, or requirements are dirty, pretend this
                # item does not exist.
                self.debug('Suppressing cache result %s, recent=%s dirty=%s'
                           % (fprint, recent, sorted(list(dirty))))
                raise KeyError(fprint)
        match[0] = time.time() + extend
        co.session = result_obj.session = ss
        self.debug('Returning cached result for %s' % fprint)
        return result_obj

    def dirty_set(self, after=0):
        dirty = set(['!timedout'])
        with self.lock:
            for ts, req in self.dirty:
                if (after == 0) or (ts > after):
                    dirty |= req
        return dirty

    def mark_dirty(self, requirements):
        with self.lock:
            self.dirty.append((time.time(), set(requirements)))
        self.debug('Marked dirty: %s' % sorted(list(requirements)))

    def refresh(self, extend=0, runtime=5, event_log=None):
        if mailpile.util.LIVE_USER_ACTIVITIES > 0:
            self.debug('Skipping cache refresh, user is waiting.')
            return

        started = now = time.time()
        with self.lock:
            # Expire things from the cache
            expired = set([f for f in self.cache if self.cache[f][0] < now])
            for fp in expired:
                del self.cache[fp]

            # Expire things from the dirty set
            self.dirty = [(ts, req) for ts, req in self.dirty
                          if ts >= (now - self._dirty_ttl)]

            # Decide which fingerprints to look at this time around
            fingerprints = list(self.cache.keys())

        refreshed = []
        fingerprints.sort(key=lambda k: -self.cache[k][0])
        for fprint in fingerprints:
            req = None
            try:
                e, req, ss, co, ro, a = self.cache[fprint]
                now = time.time()
                dirty = (req & self.dirty_set(after=a))
                if (a + self._lag < now) and dirty:
                    if now < started + runtime:
                        play_nice_with_threads()
                        co.session = ro.session = ss
                        ro = co.refresh()
                        if extend > 0:
                            e = min(e + extend, now + 5*extend)
                        if '!timedout' in req:
                            req.remove('!timedout')
                        with self.lock:
                            # Make sure we do not overwrite new results from
                            # elsewhere at this time.
                            if self.cache[fprint][-1] == a:
                                e = max(e, self.cache[fprint][0])  # Clobber?
                                self.cache[fprint] = [e, req, ss, co, ro, now]
                            refreshed.append(fprint)
                    else:
                        # Out of time, mark as dirty.
                        req.add('!timedout')
            except (ValueError, IndexError, TypeError):
                # Treat broken things as if they had timed out
                if req:
                    req.add('!timedout')

        if refreshed and event_log:
            event_log.log(message=_('New results are available'),
                          source=self,
                          data={'cache_ids': refreshed},
                          flags=Event.COMPLETE)
            self.debug('Refreshed: %s' % refreshed)


class Cached(Command):
    """Fetch results from the command cache."""
    SYNOPSIS = (None, 'cached', 'cached', '[<cache-id>]')
    ORDER = ('Internals', 7)
    HTTP_QUERY_VARS = {'id': 'Cache ID of command to redisplay'}
    IS_USER_ACTIVITY = False
    LOG_NOTHING = True

    def max_age(self):
        # Allow result to be cached by the browser for 2 seconds; we do
        # this to facilitate cross-tab sharing of cache results.
        return 2

    # Warning: This depends on internals of Command, how things are run there.
    def run(self):
        try:
            cid = self.args[0] if self.args else self.data.get('id', [None])[0]
            rv = self.session.config.command_cache.get_result(cid)
            self.session.copy(rv.session)
            rv.session.ui.render_mode = self.session.ui.render_mode
            return rv
        except:
            self._starting()
            self._error(self.FAILURE % {'name': self.name,
                                        'args': ' '.join(self.args)})
            return self._finishing(False)


_plugins.register_commands(Cached)


================================================
FILE: mailpile/commands.py
================================================
# The basic Mailpile command framework.
#
# TODO: Merge with plugins/ the division is obsolete and artificial.
#
import json
import os
import re
import shlex
import traceback
import time

import mailpile.util
import mailpile.security as security
from mailpile.crypto.gpgi import GnuPG
from mailpile.eventlog import Event
from mailpile.i18n import gettext as _
from mailpile.i18n import ngettext as _n
from mailpile.util import *
from mailpile.vfs import vfs


# Commands starting with _ don't get single-letter shortcodes...
COMMANDS = []
COMMAND_GROUPS = ['Internals', 'Config', 'Searching', 'Tagging', 'Composing']


class Command(object):
    """Generic command object all others inherit from"""
    SYNOPSIS = (None,     # CLI shortcode, e.g. A:
                None,     # CLI shortname, e.g. add
                None,     # API endpoint, e.g. sys/addmailbox
                None)     # Positional argument list
    SYNOPSIS_ARGS = None  # New-style positional argument list
    API_VERSION = None
    UI_CONTEXT = None
    IS_USER_ACTIVITY = False
    IS_HANGING_ACTIVITY = False
    IS_INTERACTIVE = False
    CONFIG_REQUIRED = True

    COMMAND_CACHE_TTL = 0   # < 1 = Not cached
    CHANGES_SESSION_CONTEXT = False

    FAILURE = 'Failed: %(name)s %(args)s'
    ORDER = (None, 0)
    SPLIT_ARG = True  # Uses shlex by default
    RAISES = (UsageError, UrlRedirectException)
    WITH_CONTEXT = ()
    COMMAND_SECURITY = None

    # Event logging settings
    LOG_NOTHING = False
    LOG_ARGUMENTS = True
    LOG_PROGRESS = False
    LOG_STARTING = '%(name)s: Starting'
    LOG_FINISHED = '%(name)s: %(message)s'

    # HTTP settings (note: security!)
    HTTP_CALLABLE = ('GET', )
    HTTP_POST_VARS = {}
    HTTP_QUERY_VARS = {}
    HTTP_BANNED_VARS = {}
    HTTP_STRICT_VARS = True
    HTTP_AUTH_REQUIRED = True

    class CommandResult:
        def __init__(self, command_obj, session,
                     command_name, doc, result, status, message,
                     template_id=None, kwargs={}, error_info={}):
            self.session = session
            self.command_obj = command_obj
            self.command_name = command_name
            self.kwargs = {}
            self.kwargs.update(kwargs)
            self.template_id = template_id
            self.doc = doc
            self.result = result
            self.status = status
            self.error_info = {}
            self.error_info.update(error_info)
            self.message = message
            self.rendered = {}
            self.renderers = {
                'json': self.as_json,
                'html': self.as_html,
                'text': self.as_text,
                'css': self.as_css,
                'csv': self.as_csv,
                'rss': self.as_rss,
                'xml': self.as_xml,
                'txt': self.as_txt,
                'js': self.as_js
            }

        def __nonzero__(self):
            return (self.result and True or False)

        def as_(self, what, *args, **kwargs):
            if args or kwargs:
                # Args render things un-cacheable.
                return self.renderers.get(what)(*args, **kwargs)

            if what not in self.rendered:
                self.rendered[what] = self.renderers.get(what, self.as_text)()
            return self.rendered[what]

        def as_text(self):
            if isinstance(self.result, bool):
                happy = '%s: %s' % (self.result and _('OK') or _('Failed'),
                                    self.message or self.doc)
                if not self.result and self.error_info:
                    return '%s\n%s' % (happy,
                        json.dumps(self.error_info, indent=4,
                                   default=mailpile.util.json_helper))
                else:
                    return happy
            elif isinstance(self.result, (dict, list, tuple)):
                return json.dumps(self.result, indent=4, sort_keys=True,
                    default=mailpile.util.json_helper)
            else:
                return unicode(self.result)

        __str__ = lambda self: self.as_text()

        __unicode__ = lambda self: self.as_text()

        def as_dict(self):
            from mailpile.urlmap import UrlMap
            um = UrlMap(self.session)
            rv = {
                'command': self.command_name,
                'state': {
                    'command_url': um.ui_url(self.command_obj),
                    'context_url': um.context_url(self.command_obj),
                    'query_args': self.command_obj.state_as_query_args(),
                    'cache_id': self.command_obj.cache_id(),
                    'context': self.command_obj.context or ''
                },
                'status': self.status,
                'message': self.message,
                'result': self.result,
                'event_id': self.command_obj.event.event_id,
                'elapsed': '%.3f' % self.session.ui.time_elapsed,
            }
            csrf_token = self.session.ui.html_variables.get('csrf_token')
            if csrf_token:
                rv['state']['csrf_token'] = csrf_token
            if self.error_info:
                rv['error'] = self.error_info
            for ui_key in [k for k in self.command_obj.data.keys()
                           if k.startswith('ui_')]:
                rv[ui_key] = self.command_obj.data[ui_key][0]
            ev = self.command_obj.event
            if ev and ev.data.get('password_needed'):
                rv['password_needed'] = ev.private_data['password_needed']
            return rv

        def as_csv(self, template=None, result=None):
            result = self.result if (result is None) else result
            if (isinstance(result, (list, tuple)) and
                    (not result or isinstance(result[0], (list, tuple)))):
                import csv, StringIO
                output = StringIO.StringIO()
                writer = csv.writer(output, dialect='excel')
                for row in result:
                    writer.writerow([unicode(r).encode('utf-8') for r in row])
                return output.getvalue().decode('utf-8')
            else:
                return ''

        def as_json(self):
            return self.session.ui.render_json(self.as_dict())

        def as_html(self, template=None):
            return self.as_template('html', template)

        def as_js(self, template=None):
            return self.as_template('js', template)

        def as_css(self, template=None):
            return self.as_template('css', template)

        def as_rss(self, template=None):
            return self.as_template('rss', template)

        def as_xml(self, template=None):
            return self.as_template('xml', template)

        def as_txt(self, template=None):
            return self.as_template('txt', template)

        def as_template(self, ttype,
                        mode=None, wrap_in_json=False, template=None):
            cache_id = ''.join(('j' if wrap_in_json else '', ttype,
                                '/' if template else '', template or '',
                                ':', mode or 'full'))
            if cache_id in self.rendered:
                return self.rendered[cache_id]
            tpath = self.command_obj.template_path(
                ttype, template_id=self.template_id, template=template)

            data = self.as_dict()
            data['title'] = self.message
            data['render_mode'] = mode or 'full'
            data['render_template'] = template or 'index'

            rendering = self.session.ui.render_web(self.session.config,
                                                   [tpath], data)
            if wrap_in_json:
                data['result'] = rendering
                self.rendered[cache_id] = self.session.ui.render_json(data)
            else:
                self.rendered[cache_id] = rendering

            return self.rendered[cache_id]

    def __init__(self, session, name=None, arg=None, data=None, async=False):
        self.session = session
        self.context = None
        self.name = self.SYNOPSIS[1] or self.SYNOPSIS[2] or name
        self.data = data or {}
        self.status = 'unknown'
        self.message = name
        self.error_info = {}
        self.result = None
        self.run_async = async
        if type(arg) in (type(list()), type(tuple())):
            self.args = tuple(arg)
        elif arg:
            if self.SPLIT_ARG is True:
                try:
                    self.args = tuple([a.decode('utf-8') for a in
                                       shlex.split(arg.encode('utf-8'))])
                except (ValueError, UnicodeEncodeError, UnicodeDecodeError):
                    raise UsageError(_('Failed to parse arguments'))
            else:
                self.args = (arg, )
        else:
            self.args = tuple([])
        if 'arg' in self.data:
            self.args = tuple(list(self.args) + self.data['arg'])
        self._create_event()

    def state_as_query_args(self):
        args = {}
        if self.args:
            args['arg'] = self._sloppy_copy(self.args)
        args.update(self._sloppy_copy(self.data))
        return args

    def cache_id(self, sqa=None):
        if self.COMMAND_CACHE_TTL < 1:
            return ''
        from mailpile.urlmap import UrlMap
        args = sorted(list((sqa or self.state_as_query_args()).iteritems()))
        args += '/%d' % self.session.ui.term.max_width
        # The replace() stuff makes these usable as CSS class IDs
        return ('%s-%s' % (UrlMap(self.session).ui_url(self),
                           md5_hex(str(args))
                           )).replace('/', '-').replace('.', '-')

    def cache_requirements(self, result):
        raise NotImplementedError('Cachable commands should override this, '
                                  'returning a set() of requirements.')

    def cache_result(self, result):
        if self.COMMAND_CACHE_TTL > 0:
            try:
                cache_id = self.cache_id()
                if cache_id:
                    self.session.config.command_cache.cache_result(
                        cache_id,
                        time.time() + self.COMMAND_CACHE_TTL,
                        self.cache_requirements(result),
                        self,
                        result)
                    self.session.ui.mark(_('Cached result as %s') % cache_id)
            except (ValueError, KeyError, TypeError, AttributeError):
                self._ignore_exception()

    def template_path(self, ttype, template_id=None, template=None):
        path_parts = (template_id or self.SYNOPSIS[2] or 'command').split('/')
        if template in (None, ttype, 'as.' + ttype):
            path_parts.append('index')
        else:
            # Security: The template request may come from the URL, so we
            #           sanitize it very aggressively before heading off
            #           to the filesystem.
            clean_tpl = CleanText(template.replace('.%s' % ttype, ''),
                                  banned=(CleanText.FS +
                                          CleanText.WHITESPACE))
            path_parts.append(clean_tpl.clean)
        path_parts[-1] += '.' + ttype
        return os.path.join(*path_parts)

    def _gnupg(self, **kwargs):
        return GnuPG(self.session.config, event=self.event, **kwargs)

    def _config(self):
        session, config = self.session, self.session.config
        if not config.loaded_config:
            config.load(session)
            parent = session
            config.prepare_workers(session, daemons=self.IS_INTERACTIVE)
        if self.IS_INTERACTIVE and not config.daemons_started():
            config.prepare_workers(session, daemons=True)
        return config

    def _idx(self, reset=False, wait=True, wait_all=True, quiet=False):
        session, config = self.session, self._config()

        if not reset and config.index:
            return config.index

        def __do_load2():
            config.vcards.load_vcards(session)
            if not wait_all:
                session.ui.report_marks(quiet=quiet)

        def __do_load1():
            with config.interruptable_wait_for_lock():
                if reset:
                    config.index = None
                    session.results = []
                    session.searched = []
                    session.displayed = None
                idx = config.get_index(session)
                if wait_all:
                    __do_load2()
                if not wait:
                    session.ui.report_marks(quiet=quiet)
                return idx

        if wait:
            rv = __do_load1()
            session.ui.reset_marks(quiet=quiet)
        else:
            config.save_worker.add_task(session, 'Load', __do_load1)
            rv = None

        if not wait_all:
            config.save_worker.add_task(session, 'Load2', __do_load2)

        return rv

    def _background_save(self,
                         everything=False, config=False,
                         index=False, index_full=False,
                         wait=False, wait_callback=None):
        session, cfg = self.session, self.session.config
        aut = cfg.save_worker.add_unique_task
        if everything or config:
            aut(session,
                'Save config',
                lambda: cfg.save(session, force=(config == '!FORCE')),
                first=True)
        if cfg.index:
            cfg.flush_mbox_cache(session, clear=False, wait=wait)
            if index_full:
                aut(session, 'Save index',
                    lambda: self._idx().save(session),
                    first=True)
            elif everything or index:
                aut(session, 'Save index changes',
                    lambda: self._idx().save_changes(session),
                    first=True)
        if wait:
            wait_callback = wait_callback or (lambda: True)
            cfg.save_worker.do(session, 'Waiting', wait_callback)

    def _choose_messages(self, words, allow_ephemeral=False):
        msg_ids = set()
        all_words = []
        for word in words:
            all_words.extend(word.split(','))
        for what in all_words:
            if what.lower() == 'these':
                if self.session.displayed:
                    b = self.session.displayed['stats']['start'] - 1
                    c = self.session.displayed['stats']['count']
                    msg_ids |= set(self.session.results[b:b + c])
                else:
                    self.session.ui.warning(_('No results to choose from!'))
            elif what.lower() in ('all', '!all', '=!all'):
                if self.session.results:
                    msg_ids |= set(self.session.results)
                else:
                    self.session.ui.warning(_('No results to choose from!'))
            elif what.startswith('='):
                try:
                    msg_id = int(what.replace('=', ''), 36)
                    if msg_id >= 0 and msg_id < len(self._idx().INDEX):
                        msg_ids.add(msg_id)
                    else:
                        self.session.ui.warning((_('No such ID: %s')
                                                 ) % (what[1:], ))
                except ValueError:
                    if allow_ephemeral and '-' in what:
                        msg_ids.add(what[1:])
                    else:
                        self.session.ui.warning(_('What message is %s?'
                                                  ) % (what, ))
            elif '-' in what:
                try:
                    b, e = what.split('-')
                    msg_ids |= set(self.session.results[int(b) - 1:int(e)])
                except (ValueError, KeyError, IndexError, TypeError):
                    self.session.ui.warning(_('What message is %s?'
                                              ) % (what, ))
            else:
                try:
                    msg_ids.add(self.session.results[int(what) - 1])
                except (ValueError, KeyError, IndexError, TypeError):
                    self.session.ui.warning(_('What message is %s?'
                                              ) % (what, ))
        return msg_ids

    def _error(self, message, info=None, result=None):
        self.status = 'error'
        self.message = message

        ui_message = _('%s error: %s') % (self.name, message)
        if info:
            self.error_info.update(info)
            details = ' '.join(['%s=%s' % (k, info[k]) for k in info])
            ui_message += ' (%s)' % details
        self.session.ui.mark(self.name)
        self.session.ui.error(ui_message)

        if result:
            return self.view(result)
        else:
            return False

    def _success(self, message, result=True):
        self.status = 'success'
        self.message = message

        ui_message = '%s: %s' % (self.name, message)
        self.session.ui.mark(ui_message)

        return self.view(result)

    def _read_file_or_data(self, fn):
        if fn in self.data:
            return self.data[fn]
        else:
            return vfs.open(fn, 'rb').read()

    def _ignore_exception(self):
        self.session.ui.debug(traceback.format_exc())

    def _serialize(self, name, function):
        return function()

    def _background(self, name, function):
        session, config = self.session, self.session.config
        return config.scan_worker.add_task(session, name, function)

    def _update_event_state(self, state, log=False):
        self.event.flags = state
        self.event.data['elapsed'] = int(1000 * (time.time()-self._start_time))

        if (log or self.LOG_PROGRESS) and not self.LOG_NOTHING:
            self.event.data['ui'] = str(self.session.ui.__class__.__name__)
            self.event.data['output'] = self.session.ui.render_mode
            if self.session.config.event_log:
                self.session.config.event_log.log_event(self.event)

    def _starting(self):
        self._start_time = time.time()
        self._update_event_state(Event.RUNNING)
        if self.name:
            self.session.ui.start_command(self.name, self.args, self.data)

    def _fmt_msg(self, message):
        return message % {'name': self.name,
                          'status': self.status or '',
                          'message': self.message or ''}

    def _sloppy_copy(self, data, name=None):
        if name and (name[:4] in ('pass', 'csrf') or
                     'password' in name or
                     'passphrase' in name):
            data = '(SUPPRESSED)'
        def copy_value(v):
            try:
                unicode(v).encode('utf-8')
                return unicode(v)[:1024]
            except (UnicodeEncodeError, UnicodeDecodeError):
                return '(BINARY DATA)'
        if isinstance(data, (list, tuple)):
            return [self._sloppy_copy(i, name=name) for i in data]
        elif isinstance(data, dict):
            return dict((k, self._sloppy_copy(v, name=k))
                        for k, v in data.iteritems())
        else:
            return copy_value(data)

    def _create_event(self):
        private_data = {}
        if self.LOG_ARGUMENTS:
            if self.data:
                private_data['data'] = self._sloppy_copy(self.data)
            if self.args:
                private_data['args'] = self._sloppy_copy(self.args)

        self.event = self._make_command_event(private_data)

    def _make_command_event(self, private_data):
        return Event(source=self,
                     message=self._fmt_msg(self.LOG_STARTING),
                     flags=Event.INCOMPLETE,
                     data={},
                     private_data=private_data)

    def _finishing(self, rv, just_cleanup=False):
        if just_cleanup:
            self._update_finished_event()
            return rv

        if not self.context:
            self.context = self.session.get_context(
                update=self.CHANGES_SESSION_CONTEXT)

        self.session.ui.mark(_('Generating result'))
        result = self.CommandResult(self, self.session, self.name,
                                    self.__doc__,
                                    rv, self.status, self.message,
                                    error_info=self.error_info)
        self.cache_result(result)

        if not self.run_async:
            self._update_finished_event()
        self.session.last_event_id = self.event.event_id
        return result

    def _update_finished_event(self):
        # Update the event!
        if self.message:
            self.event.message = self.message
        if self.error_info:
            self.event.private_data['error_info'] = self.error_info
        self.event.message = self._fmt_msg(self.LOG_FINISHED)
        self._update_event_state(Event.COMPLETE, log=True)

        self.session.ui.mark(self.event.message)
        self.session.ui.report_marks(
            details=('timing' in self.session.config.sys.debug))
        if self.name:
            self.session.ui.finish_command(self.name)

    def _run_sync(self, enable_cache, *args, **kwargs):
        try:
            thread_context_push(command=self,
                                event=self.event,
                                session=self.session)
            self._starting()
            self._run_args = args
            self._run_kwargs = kwargs

            if (self.COMMAND_CACHE_TTL > 0 and
                   'http' not in self.session.config.sys.debug and
                   enable_cache):
                cid = self.cache_id()
                try:
                    rv = self.session.config.command_cache.get_result(cid)
                    rv.session.ui = self.session.ui
                    if self.CHANGES_SESSION_CONTEXT:
                        self.session.copy(rv.session)
                    self.session.ui.mark(_('Using pre-cached result object %s') % cid)
                    self._finishing(True, just_cleanup=True)
                    return rv
                except:
                    pass

            def command(self, *args, **kwargs):
                if self.CONFIG_REQUIRED:
                    if not self.session.config.loaded_config:
                        return self._error(_('Please log in'))
                    if mailpile.util.QUITTING:
                        return self._error(_('Shutting down'))
                return self.command(*args, **kwargs)

            return self._finishing(command(self, *args, **kwargs))
        except self.RAISES:
            self.status = 'success'
            self._finishing(True, just_cleanup=True)
            raise
        except:
            self._ignore_exception()
            self._error(self.FAILURE % {'name': self.name,
                                        'args': ' '.join(self.args)})
            return self._finishing(False)
        finally:
            thread_context_pop()

    def _run(self, *args, **kwargs):
        if self.run_async:
            def streetcar():
                try:
                    with MultiContext(self.WITH_CONTEXT):
                        rv = self._run_sync(True, *args, **kwargs).as_dict()
                        self.event.private_data.update(rv)
                        self._update_finished_event()
                except:
                    traceback.print_exc()

            self._starting()
            self._update_event_state(self.event.RUNNING, log=True)
            result = Command.CommandResult(self, self.session, self.name,
                                           self.__doc__,
                                           {"resultid": self.event.event_id},
                                           "success",
                                           "Running in background")

            self.session.config.scan_worker.add_task(self.session, self.name,
                                                     streetcar, first=True)
            return result

        else:
            return self._run_sync(True, *args, **kwargs)

    def _maybe_trigger_cache_refresh(self):
        if self.data.get('_method') == 'POST':
            def refresher():
                self.session.config.command_cache.refresh(
                    event_log=self.session.config.event_log)
            self.session.config.scan_worker.add_unique_task(
                self.session, 'post-refresh', refresher, first=True)

    def record_user_activity(self):
        mailpile.util.LAST_USER_ACTIVITY = time.time()

    def run(self, *args, **kwargs):
        if self.COMMAND_SECURITY is not None:
            forbidden = security.forbid_command(self)
            if forbidden:
                return self._error(forbidden)

        with MultiContext(self.WITH_CONTEXT):
            if self.IS_USER_ACTIVITY:
                try:
                    self.record_user_activity()
                    mailpile.util.LIVE_USER_ACTIVITIES += 1
                    rv = self._run(*args, **kwargs)
                    self._maybe_trigger_cache_refresh()
                    return rv
                finally:
                    mailpile.util.LIVE_USER_ACTIVITIES -= 1
            else:
                rv = self._run(*args, **kwargs)
                self._maybe_trigger_cache_refresh()
                return rv

    def refresh(self):
        self._create_event()
        return self._run_sync(False, *self._run_args, **self._run_kwargs)

    def command(self):
        return None

    def etag_data(self):
        return []

    def max_age(self):
        return 0

    @classmethod
    def view(cls, result):
        return result


def GetCommand(name):
    match = [c for c in COMMANDS if name in c.SYNOPSIS[:3]]
    if len(match) == 1:
        return match[0]
    return None


def Action(session, opt, arg, data=None):
    session.ui.reset_marks(quiet=True)
    config = session.config

    if not opt:
        return Help(session, 'help').run()

    # Use the COMMANDS dict by default.
    command = GetCommand(opt)
    if command:
        return command(session, opt, arg, data=data).run()

    # Tags are commands
    if config.loaded_config:
        lopt = opt.lower()

        found = None
        for tag in config.tags.values():
            if lopt == tag.slug.lower():
                found = tag
                break
        if not found:
            for tag in config.tags.values():
                if lopt == tag.name.lower():
                    found = tag
                    break
        if not found:
            for tag in config.tags.values():
                if lopt == _(tag.name).lower():
                    found = tag
                    break

        if found:
            a = 'in:%s%s%s' % (found.slug, ' ' if arg else '', arg)
            return GetCommand('search')(session, opt,
                                        arg=a, data=data).run()

    # OK, give up!
    raise UsageError(_('Unknown command: %s') % opt)


================================================
FILE: mailpile/config/__init__.py
================================================


================================================
FILE: mailpile/config/base.py
================================================
from __future__ import print_function
import io
import json
import os
import ConfigParser
from urllib import quote, unquote

from mailpile.i18n import gettext as _
from mailpile.i18n import ngettext as _n
from mailpile.util import *

import mailpile.config.validators as validators


class ConfigValueError(ValueError):
    pass


def ConfigRule(*args):
    class _ConfigRule(list):
        def __init__(self):
            list.__init__(self, args)
            self._types = []
    return _ConfigRule()


def PublicConfigRule(*args):
    c = ConfigRule(*args)
    c._types.append('public')
    return c


def KeyConfigRule(*args):
    c = ConfigRule(*args)
    c._types.append('key')
    return c


# FIXME: This should be enforced somehow when variables are altered.
#        Run in a context?
def CriticalConfigRule(*args):
    c = ConfigRule(*args)
    c._types += ['critical']
    return c


def ConfigPrinter(cfg, indent=''):
    rv = []
    if isinstance(cfg, dict):
        pairer = cfg.iteritems()
    else:
        pairer = enumerate(cfg)
    for key, val in pairer:
        if hasattr(val, 'rules'):
            preamble = '[%s: %s] ' % (val._NAME, val._COMMENT)
        else:
            preamble = ''
        if isinstance(val, (dict, list, tuple)):
            if isinstance(val, dict):
                b, e = '{', '}'
            else:
                b, e = '[', ']'
            rv.append(('%s: %s%s\n%s\n%s'
                       '' % (key, preamble, b, ConfigPrinter(val, '  '), e)
                       ).replace('\n  \n', ''))
        elif isinstance(val, (str, unicode)):
            rv.append('%s: "%s"' % (key, val))
        else:
            rv.append('%s: %s' % (key, val))
    return indent + ',\n'.join(rv).replace('\n', '\n'+indent)


class InvalidKeyError(ValueError):
    pass


class CommentedEscapedConfigParser(ConfigParser.RawConfigParser):
    """
    This is a ConfigParser that allows embedded comments and safely escapes
    and encodes/decodes values that include funky characters.

    >>> cfg = u'[config/sys: Stuff]\\ndebug = True ; Ignored comment'
    >>> cecp = CommentedEscapedConfigParser()
    >>> cecp.readfp(io.BytesIO(cfg.encode('utf-8')))
    >>> cecp.get('config/sys: Stuff', 'debug') == 'True'
    True

    >>> cecp.items('config/sys: Stuff')
    [(u'debug', u'True')]
    """
    NOT_UTF8 = '%C0'  # This byte is never valid at the start of an utf-8
                      # string, so we use it to mark binary data.
    SAFE = '!?: /#@<>[]()=-'

    def set(self, section, key, value, comment):
        key = unicode(key).encode('utf-8')
        section = unicode(section).encode('utf-8')

        if isinstance(value, unicode):
            value = quote(value.encode('utf-8'), safe=self.SAFE)
        elif isinstance(value, str):
            quoted = quote(value, safe=self.SAFE)
            if quoted != value:
                value = self.NOT_UTF8 + quoted
        else:
            value = quote(unicode(value).encode('utf-8'), safe=self.SAFE)

        if value.endswith(' '):
            value = value[:-1] + '%20'
        if comment:
            pad = ' ' * (25 - len(key) - len(value)) + ' ; '
            value = '%s%s%s' % (value, pad, comment)
        return ConfigParser.RawConfigParser.set(self, section, key, value)

    def _decode_value(self, value):
        if value.startswith(self.NOT_UTF8):
            return unquote(value[len(self.NOT_UTF8):])
        else:
            return unquote(value).decode('utf-8')

    def get(self, section, key):
        key = unicode(key).encode('utf-8')
        section = unicode(section).encode('utf-8')
        value = ConfigParser.RawConfigParser.get(self, section, key)
        return self._decode_value(value)

    def items(self, section):
        return [(k.decode('utf-8'), self._decode_value(i)) for k, i
                in ConfigParser.RawConfigParser.items(self, section)]


def _MakeCheck(pcls, name, comment, rules, write_watcher):
    class Checker(pcls):
        _NAME = name
        _RULES = rules
        _COMMENT = comment
        _WWATCHER = write_watcher
    return Checker


def RuledContainer(pcls):
    """
    Factory for abstract 'container with rules' class. See ConfigDict for
    details, examples and tests.
    """

    class _RuledContainer(pcls):
        RULE_COMMENT = 0
        RULE_CHECKER = 1
        # Reserved ...
        RULE_DEFAULT = -1
        RULE_CHECK_MAP = {
            bool: validators.BoolCheck,
            'bin': validators.NotUnicode,
            'bool': validators.BoolCheck,
            'b36': validators.B36Check,
            'dir': validators.DirCheck,
            'directory': validators.DirCheck,
            'ignore': validators.IgnoreCheck,
            'email': validators.EmailCheck,
            'False': False, 'false': False,
            'file': validators.FileCheck,
            'float': float,
            'gpgkeyid': validators.GPGKeyCheck,
            'hostname': validators.HostNameCheck,
            'int': int,
            'long': long,
            'multiline': unicode,
            'new file': validators.NewPathCheck,
            'new dir': validators.NewPathCheck,
            'new directory': validators.NewPathCheck,
            'path': validators.PathCheck,
            str: unicode,
            'slashslug': validators.SlashSlugCheck,
            'slug': validators.SlugCheck,
            'str': unicode,
            'True': True, 'true': True,
            'timestamp': long,
            'unicode': unicode,
            'url': validators.UrlCheck, # FIXME: check more than the scheme?
            'webroot': validators.WebRootCheck
        }
        def _default_write_watcher(self, *args):
            self._changed = True

        _NAME = 'container'
        _RULES = None
        _COMMENT = None
        _MAGIC = True
        _WWATCHER = _default_write_watcher

        def __init__(self, *args, **kwargs):
            rules = kwargs.get('_rules', self._RULES or {})
            self._name = kwargs.get('_name', self._NAME)
            self._comment = kwargs.get('_comment', self._COMMENT)
            self._write_watcher = kwargs.get('_write_watcher', self._WWATCHER)
            enable_magic = kwargs.get('_magic', self._MAGIC)
            for kw in ('_rules', '_comment', '_name', '_magic', '_write_watcher'):
                if kw in kwargs:
                    del kwargs[kw]

            pcls.__init__(self)
            self._key = self._name
            self._rules_source = rules
            self._changed = False
            self.rules = {}
            self.set_rules(rules)
            self.update(*args, **kwargs)

            self._magic = enable_magic  # Enable the getitem/getattr magic

        def __str__(self):
            return json.dumps(self, sort_keys=True, indent=2)

        def __unicode__(self):
            return json.dumps(self, sort_keys=True, indent=2)

        def as_config_bytes(self, _type=None, _xtype=None):
            of = io.BytesIO()
            self.as_config(_type=_type, _xtype=_xtype).write(of)
            return of.getvalue()

        def key_types(self, key):
            if key not in self.rules:
                key = '_any'
            if key in self.rules and hasattr(self.rules[key], '_types'):
                return self.rules[key]._types
            else:
                return []

        def as_config(self, config=None, _type=None, _xtype=None):
            config = config or CommentedEscapedConfigParser()
            section = self._name
            if self._comment:
                section += ': %s' % self._comment
            added_section = False

            keys = self.rules.keys()
            if _type:
                keys = [k for k in keys if _type in self.key_types(k)]

            ignore = self.ignored_keys() | set(['_any'])
            if not _type:
                if not keys or '_any' in keys:
                    keys.extend(self.keys())

            keys = [k for k in sorted(set(keys)) if k not in ignore]
            set_keys = set(self.keys())

            for key in keys:
                if not hasattr(self[key], 'as_config'):
                    if key in self.rules:
                        comment = self.rules[key][self.RULE_COMMENT]
                    else:
                        comment = ''
                    value = self[key]
                    if value is not None and value != '':
                        if key not in set_keys:
                            key = ';' + key
                            comment = '(default) ' + comment
                        if not added_section:
                            config.add_section(str(section))
                            added_section = True
                        if _xtype not in self.key_types(key) or not _xtype:
                            config.set(section, key, value, comment)
            for key in keys:
                if hasattr(self[key], 'as_config'):
                    if isinstance(self[key], list):
                        # If a list is marked public, we export all items
                        self[key].as_config(config=config)
                    else:
                        self[key].as_config(
                            config=config, _type=_type, _xtype=_xtype)

            return config

        def reset(self, rules=True, data=True):
            raise Exception(_('Please override this method'))

        def set_rules(self, rules):
            safe_assert(isinstance(rules, dict))
            self.reset()
            for key, rule in rules.iteritems():
                self.add_rule(key, rule)

        def add_rule(self, key, rule):
            if not ((isinstance(rule, (list, tuple))) and
                    (key == CleanText(key, banned=CleanText.NONVARS).clean) and
                    (not self.real_hasattr(key))):
                raise TypeError('add_rule(%s, %s): Bad key or rule.'
                                % (key, rule))

            orule, rule = rule, ConfigRule(*rule[:])
            if hasattr(orule, '_types'):
                rule._types = orule._types

            self.rules[key] = rule
            check = rule[self.RULE_CHECKER]
            try:
                check = self.RULE_CHECK_MAP.get(check, check)
                rule[self.RULE_CHECKER] = check
            except TypeError:
                pass

            name = '%s/%s' % (self._name, key)
            comment = rule[self.RULE_COMMENT]
            value = rule[self.RULE_DEFAULT]
            ww = self.real_getattr('_write_watcher')

            if (isinstance(check, dict) and value is not None
                    and not isinstance(value, (dict, list))):
                raise TypeError(_('Only lists or dictionaries can contain '
                                  'dictionary values (key %s).') % name)

            if isinstance(value, dict) and check is False:
                pcls.__setitem__(self, key, ConfigDict(_name=name,
                                                       _comment=comment,
                                                       _write_watcher=ww,
                                                       _rules=value))

            elif isinstance(value, dict):
                if value:
                    raise ConfigValueError(_('Subsections must be immutable '
                                             '(key %s).') % name)
                sub_rule = {'_any': [rule[self.RULE_COMMENT], check, None]}
                checker = _MakeCheck(ConfigDict, name, check, sub_rule, ww)
                pcls.__setitem__(self, key, checker())
                rule[self.RULE_CHECKER] = checker

            elif isinstance(value, list):
                if value:
                    raise ConfigValueError(_('Lists cannot have default '
                                             'values (key %s).') % name)
                sub_rule = {'_any': [rule[self.RULE_COMMENT], check, None]}
                checker = _MakeCheck(ConfigList, name, comment, sub_rule, ww)
                pcls.__setitem__(self, key, checker())
                rule[self.RULE_CHECKER] = checker

            elif not isinstance(value, (type(None), int, long, bool,
                                        float, str, unicode)):
                raise TypeError(_('Invalid type "%s" for key "%s" (value: %s)'
                                  ) % (type(value), name, repr(value)))

        def __fixkey__(self, key):
            return key.lower()

        def fmt_key(self, key):
            return key.lower()

        def get_rule(self, key):
            key = self.__fixkey__(key)
            rule = self.rules.get(key, None)
            if rule is None:
                if '_any' in self.rules:
                    rule = self.rules['_any']
                else:
                    raise InvalidKeyError(_('Invalid key for %s: %s'
                                            ) % (self._name, key))
            if isinstance(rule[self.RULE_CHECKER], dict):
                rule = rule[:]
                rule[self.RULE_CHECKER] = _MakeCheck(
                    ConfigDict,
                    '%s/%s' % (self._name, key),
                    rule[self.RULE_COMMENT],
                    rule[self.RULE_CHECKER],
                    self._write_watcher)
            return rule

        def ignored_keys(self):
            return set([k for k in self.rules
                if self.rules[k][self.RULE_CHECKER] == validators.IgnoreCheck])

        def walk(self, path, parent=0, key_types=None):
            if '.' in path:
                sep = '.'
            else:
                sep = '/'
            path_parts = path.split(sep)
            cfg = self
            if parent:
                vlist = path_parts[-parent:]
                path_parts[-parent:] = []
            else:
                vlist = []
            for part in path_parts:
                if key_types is not None:
                    if [t for t in cfg.key_types(part) if t not in key_types]:
                        raise AccessError(_('Access denied to %s') % part)
                cfg = cfg[part]
            if parent:
                return tuple([cfg] + vlist)
            else:
                return cfg

        def get(self, key, default=None):
            key = self.__fixkey__(key)
            if key in self:
                return pcls.__getitem__(self, key)
            if default is None and key in self.rules:
                return self.rules[key][self.RULE_DEFAULT]
            return default

        def __getitem__(self, key):
            key = self.__fixkey__(key)
            if key in self.rules or '_any' in self.rules:
                return self.get(key)
            return pcls.__getitem__(self, key)

        def real_getattr(self, attr):
            try:
                return pcls.__getattribute__(self, attr)
            except AttributeError:
                return False

        def real_hasattr(self, attr):
            try:
                pcls.__getattribute__(self, attr)
                return True
            except AttributeError:
                return False

        def real_setattr(self, attr, value):
            return pcls.__setattr__(self, attr, value)

        def __getattr__(self, attr, default=None):
            if self.real_hasattr(attr) or not self.real_getattr('_magic'):
                return pcls.__getattribute__(self, attr)
            return self[attr]

        def __setattr__(self, attr, value):
            if self.real_hasattr(attr) or not self.real_getattr('_magic'):
                return self.real_setattr(attr, value)
            self.__setitem__(attr, value)

        def __passkey__(self, key, value):
            if hasattr(value, '__passkey__'):
                value._key = key
                value._name = '%s/%s' % (self._name, key)

        def __passkey_recurse__(self, key, value):
            if hasattr(value, '__passkey__'):
                if isinstance(value, (list, tuple)):
                    for k in range(0, len(value)):
                        value.__passkey__(value.__fixkey__(k), value[k])
                elif isinstance(value, dict):
                    for k in value:
                        value.__passkey__(value.__fixkey__(k), value[k])

        def __createkey_and_setitem__(self, key, value):
            pcls.__setitem__(self, key, value)

        def __setitem__(self, key, value):
            key = self.__fixkey__(key)
            checker = self.get_rule(key)[self.RULE_CHECKER]
            if not checker is True:
                if checker is False:
                    if isinstance(value, dict) and isinstance(self[key], dict):
                        for k, v in value.iteritems():
                            self[key][k] = v
                        return
                    raise ConfigValueError(_('Modifying %s/%s is not '
                                             'allowed') % (self._name, key))
                elif isinstance(checker, (list, set, tuple)):
                    if value not in checker:
                        raise ConfigValueError(_('Invalid value for %s/%s: %s'
                                                 ) % (self._name, key, value))
                elif isinstance(checker, (type, type(RuledContainer))):
                    try:
                        if value is None:
                            value = checker()
                        else:
                            value = checker(value)
                    except (ConfigValueError):
                        raise
                    except (validators.IgnoreValue):
                        return
                    except (ValueError, TypeError):
                        raise ValueError(_('Invalid value for %s/%s: %s'
                                           ) % (self._name, key, value))
                else:
                    raise Exception(_('Unknown constraint for %s/%s: %s'
                                      ) % (self._name, key, checker))

            write_watcher = self.real_getattr('_write_watcher')
            if write_watcher is not None:
                write_watcher(self, key, value)

            self.__passkey__(key, value)
            self.__createkey_and_setitem__(key, value)
            self.__passkey_recurse__(key, value)

        def extend(self, src):
            for val in src:
                self.append(val)

        def __iadd__(self, src):
            self.extend(src)
            return self

    return _RuledContainer


class ConfigList(RuledContainer(list)):
    """
    A sanity-checking, self-documenting list of program settings.

    Instances of this class are usually contained within a ConfigDict.

    >>> lst = ConfigList(_rules={'_any': ['We only like ints', int, 0]})
    >>> lst.append('1')
    '0'
    >>> lst.extend([2, '3'])
    >>> lst
    [1, 2, 3]

    >>> lst += ['1', '2']
    >>> lst
    [1, 2, 3, 1, 2]

    >>> lst.extend(range(0, 100))
    >>> lst['c'] == lst[int('c', 36)]
    True
    """
    def reset(self, rules=True, data=True):
        if rules:
            self.rules = {}
        if data:
            self[:] = []

    def __createkey_and_setitem__(self, key, value):
        while key > len(self):
            self.append(self.rules['_any'][self.RULE_DEFAULT])
        if key == len(self):
            self.append(value)
        else:
            list.__setitem__(self, key, value)

    def append(self, value):
        list.append(self, None)
        try:
            self[len(self) - 1] = value
            return b36(len(self) - 1).lower()
        except:
            self[len(self) - 1:] = []
            raise

    def __passkey__(self, key, value):
        if hasattr(value, '__passkey__'):
            key = b36(key).lower()
            value._key = key
            value._name = '%s/%s' % (self._name, key)

    def __fixkey__(self, key):
        if isinstance(key, (str, unicode)):
            try:
                key = int(key, 36)
            except ValueError:
                pass
        return key

    def get(self, key, default=None):
        try:
            return list.__getitem__(self, self.__fixkey__(key))
        except IndexError:
            return default

    def __getitem__(self, key):
        return list.__getitem__(self, self.__fixkey__(key))

    def fmt_key(self, key):
        f = b36(self.__fixkey__(key)).lower()
        return ('0000' + f)[-4:] if (len(f) < 4) else f

    def iterkeys(self):
        return (self.fmt_key(i) for i in range(0, len(self)))

    def iteritems(self):
        for k in self.iterkeys():
            yield (k, self[k])

    def keys(self):
        return list(self.iterkeys())

    def all_keys(self):
        return list(self.iterkeys())

    def values(self):
        return self[:]

    def update(self, *args):
        for l in args:
            l = list(l)
            for i in range(0, len(self)):
                self[i] = l[i]
            for i in range(len(self), len(l)):
                self.append(l[i])


class ConfigDict(RuledContainer(dict)):
    """
    A sanity-checking, self-documenting dictionary of program settings.

    The object must be initialized with a dictionary which describes in
    a structured way what variables exist, what their legal values are,
    and what their defaults are and what they are for.

    Each variable definition expects three values:
       1. A human readable description of what the variable is
       2. A data type / sanity check
       3. A default value

    If the sanity check is itself a dictionary of rules, values are expected
    to be dictionaries or lists of items that match the rules defined. This
    should be used with an empty list or dictionary as a default value.

    Configuration data can be nested by including a dictionary of further
    rules in place of the default value.

    If the default value is an empty list, it is assumed to be a list of
    values of the type specified.

    Examples:

    >>> pot = ConfigDict(_rules={'potatoes': ['How many potatoes?', 'int', 0],
    ...                          'carrots': ['How many carrots?', int, 99],
    ...                          'liquids': ['Fluids we like', False, {
    ...                                         'water': ['Liters', int, 0],
    ...                                         'vodka': ['Liters', int, 12]
    ...                                      }],
    ...                          'tags': ['Tags', {'c': ['C', int, 0],
    ...                                            'x': ['X', str, '']}, []],
    ...                          'colors': ['Colors', ('red', 'blue'), []]})
    >>> sorted(pot.keys()), sorted(pot.values())
    (['colors', 'liquids', 'tags'], [[], [], {}])

    >>> pot['potatoes'] = pot['liquids']['vodka'] = "123"
    >>> pot['potatoes']
    123
    >>> pot['liquids']['vodka']
    123
    >>> pot['carrots']
    99

    >>> pot.walk('liquids.vodka')
    123
    >>> pot.walk('liquids/vodka', parent=True)
    ({...}, 'vodka')

    >>> pot['colors'].append('red')
    '0'
    >>> pot['colors'].extend(['blue', 'red', 'red'])
    >>> pot['colors']
    ['red', 'blue', 'red', 'red']

    >>> pot['tags'].append({'c': '123', 'x': 'woots'})
    '0'
    >>> pot['tags'][0]['c']
    123
    >>> pot['tags'].append({'z': 'invalid'})
    Traceback (most recent call last):
        ...
    ValueError: Invalid value for config/tags/1: ...

    >>> pot['evil'] = 123
    Traceback (most recent call last):
        ...
    InvalidKeyError: Invalid key for config: evil
    >>> pot['liquids']['evil'] = 123
    Traceback (most recent call last):
        ...
    InvalidKeyError: Invalid key for config/liquids: evil
    >>> pot['potatoes'] = "moo"
    Traceback (most recent call last):
        ...
    ValueError: Invalid value for config/potatoes: moo
    >>> pot['colors'].append('green')
    Traceback (most recent call last):
        ...
    ConfigValueError: Invalid value for config/colors/4: green

    >>> pot.rules['potatoes']
    ['How many potatoes?', <type 'int'>, 0]

    >>> isinstance(pot['liquids'], ConfigDict)
    True
    """
    _NAME = 'config'

    def reset(self, rules=True, data=True):
        if rules:
            self.rules = {}
        if data:
            for key in self.keys():
                if hasattr(self[key], 'reset'):
                    self[key].reset(rules=rules, data=data)
                else:
                    dict.__delitem__(self, key)

    def all_keys(self):
        return list(set(self.keys()) | set(self.rules.keys())
                    - self.ignored_keys() - set(['_any']))

    def append(self, value):
        """Add to the dict using an autoselected key"""
        if '_any' in self.rules:
            k = b36(max([int(k, 36) for k in self.keys()] + [-1]) + 1).lower()
            self[k] = value
            return k
        else:
            raise UsageError(_('Cannot append to fixed dict'))

    def update(self, *args, **kwargs):
        """Reimplement update, so it goes through our sanity checks."""
        for src in args:
            if hasattr(src, 'keys'):
                for key in src:
                    self[key] = src[key]
            else:
                for key, val in src:
                    self[key] = val
        for key in kwargs:
            self[key] = kwargs[key]

    def parse_config(self, session, data, source='internal'):
        """
        Parse a config file fragment. Invalid data will be ignored, but will
        generate warnings in the session UI. Returns True on a clean parse,
        False if any of the settings were bogus.

        >>> cfg.parse_config(session, '[config/sys]\\nfd_cache_size = 123\\n')
        True
        >>> cfg.sys.fd_cache_size
        123

        >>> cfg.parse_config(session, '[config/bogus]\\nblabla = bla\\n')
        False
        >>> [l[1] for l in session.ui.log_buffer if 'bogus' in l[1]][0]
        'Invalid (internal): section config/bogus does not exist'

        >>> cfg.parse_config(session, '[config/sys]\\nhistory_length = 321\\n'
        ...                                          'bogus_variable = 456\\n')
        False
        >>> cfg.sys.history_length
        321
        >>> [l[1] for l in session.ui.log_buffer if 'bogus_var' in l[1]][0]
        u'Invalid (internal): section config/sys, ...
        """
        parser = CommentedEscapedConfigParser()
        parser.readfp(io.BytesIO(str(data)))

        def item_sorter(i):
            try:
                return (int(i[0], 36), i[1])
            except (ValueError, IndexError, KeyError, TypeError):
                return i

        all_okay = True
        for section in parser.sections():
            okay = True
            cfgpath = section.split(':')[0].split('/')[1:]
            cfg = self
            added_parts = []
            for part in cfgpath:
                if cfg.fmt_key(part) in cfg.keys():
                    cfg = cfg[part]
                elif '_any' in cfg.rules:
                    cfg[part] = {}
                    cfg = cfg[part]
                else:
                    if session:
                        msg = _('Invalid (%s): section %s does not '
                                'exist') % (source, section)
                        session.ui.warning(msg)
                    all_okay = okay = False
            items = parser.items(section) if okay else []
            items.sort(key=item_sorter)
            for var, val in items:
                try:
                    cfg[var] = val
                except (ValueError, KeyError, IndexError):
                    if session:
                        msg = _(u'Invalid (%s): section %s, variable %s=%s'
                                ) % (source, section, var, val)
                        session.ui.warning(msg)
                    all_okay = okay = False
        return all_okay


class PathDict(ConfigDict):
    _RULES = {
        '_any': ['Data directory', 'directory', '']
    }


if __name__ == "__main__":
    import doctest
    import sys
    import copy

    import mailpile.config.defaults
    import mailpile.ui

    rules = copy.deepcopy(mailpile.config.defaults.CONFIG_RULES)
    rules.update({
        'nest1': ['Nest1', {
            'nest2': ['Nest2', str, []],
            'nest3': ['Nest3', {
                'nest4': ['Nest4', str, []]
            }, []],
        }, {}]
    })
    cfg = ConfigDict(_rules=rules)
    session = mailpile.ui.Session(cfg)
    session.ui = mailpile.ui.SilentInteraction(cfg)
    session.ui.block()

    result = doctest.testmod(optionflags=doctest.ELLIPSIS)
    print('%s' % (result, ))
    if result.failed:
        sys.exit(1)


================================================
FILE: mailpile/config/defaults.py
================================================
from __future__ import print_function
APPVER = "1.0.0rc6"
ABOUT = """\
Mailpile.py              a tool             Copyright 2013-2018, Mailpile ehf
 v%8.0008s         for searching and               <https://www.mailpile.is/>
               organizing piles of e-mail

This program is free software: you can redistribute it and/or modify it under
the terms of either the GNU Affero General Public License as published by the
Free Software Foundation. See the file COPYING.md for details.
""" % APPVER
#############################################################################
import os
import sys
import time

from mailpile.config.base import PathDict
from mailpile.config.base import ConfigRule as c
from mailpile.config.base import CriticalConfigRule as X
from mailpile.config.base import PublicConfigRule as p
from mailpile.config.base import KeyConfigRule as k


_ = lambda string: string


DEFAULT_SENDMAIL = '|/usr/sbin/sendmail -i %(rcpt)s'
CONFIG_PLUGINS = []
CONFIG_RULES = {
    'version': p(_('Mailpile program version'), str, APPVER),
    'homedir': p(_('Location of Mailpile data'), False, '(unset)'),
    'timestamp': [_('Configuration timestamp'), int, int(time.time())],
    'master_key': k(_('Master symmetric encryption key'), str, ''),
    'sys': p(_('Technical system settings'), False, {
        'fd_cache_size': p(_('Max files kept open at once'), int,         500),
        'minfree_mb':    p(_('Required free disk space (MB)'), int,      1024),
        'history_length': (_('History length (lines, <0=no save)'), int,  100),
        'http_host':     p(_('Listening host for web UI'),
                           'hostname', 'localhost'),
        'http_port':     p(_('Listening port for web UI'), int,         33411),
        'http_path':     p(_('HTTP path of web UI'), 'webroot',            ''),
        'http_no_auth':  X(_('Disable HTTP authentication'),      bool, False),
        'ajax_timeout':   (_('AJAX Request timeout'), int,              10000),
        'postinglist_kb': (_('Posting list target size in KB'), int,       64),
        'sort_max':       (_('Max results we sort "well"'), int,         2500),
        'snippet_max':    (_('Max length of metadata snippets'), int,     275),
        'debug':         p(_('Debugging flags'), str,                      ''),
        'experiments':    (_('Enabled experiments'), str,                  ''),
        'gpg_keyserver':  (_('Host:port of PGP keyserver'),
                           str, 'pool.sks-keyservers.net'),
        'gpg_home':      p(_('Override the home directory of GnuPG'),
                           'dir', None),
        'gpg_binary':    p(_('Override the default GPG binary path'),
                           'file', None),
        'local_mailbox_id': (_('Local read/write Maildir'), 'b36',         ''),
        'mailindex_file':   (_('Metadata index file'), 'file',             ''),
        'postinglist_dir': (_('Search index directory'), 'dir',            ''),
        'mailbox':        [_('Mailboxes we index'), 'bin',                 []],
        'plugins_early': p(_('Plugins to load before login'),
                           CONFIG_PLUGINS, []),
        'plugins':        [_('Plugins to load after login'),
                           CONFIG_PLUGINS, []],
        'path':           [_('Locations of assorted data'), False, {
            'html_theme': [_('User interface theme'), 'dir', 'default-theme'],
            'vcards':     [_('Location of vCards'), 'dir', 'vcards'],
            'event_log':  [_('Location of event log'), 'dir', 'logs'],
        }],
        'lockdown':      p(_('Demo mode, disallow changes'), str,          ''),
        'login_banner':  p(_('A custom banner for the login page'), str,   ''),
        'proxy':         p(_('Proxy settings'), False, {
            'protocol':  p(_('Proxy protocol'),
                           ["tor", "tor-risky", "socks5", "socks4", "http",
                            "none", "system", "unknown"], "system"),
            'fallback':  p(_('Allow fallback to direct conns'), bool, False),
            'username':   (_('User name'), str, ''),
            'password':   (_('Password'), str, ''),
            'host':      p(_('Host'), str, ''),
            'port':      p(_('Port'), int, 8080),
            'no_proxy':  p(_('List of hosts to avoid proxying'), str,
                           'localhost, 127.0.0.1, ::1')
        }),
        'tor': p(_('Tor settings'), False, {
            'binary':    p(_('Override the default Tor binary path'),
                           'file', None),
            'systemwide':p(_('Use shared system-wide Tor (not our own)'),
                                                                   bool, True),
            'socks_host':p(_('Socks host'), str, ''),
            'socks_port':p(_('Socks Port'), int, 0),
            'ctrl_port': p(_('Control Port'), int, 0),
            'ctrl_auth': p(_('Control Password'), str, '')
        })
    }),
    'prefs': p(_("User preferences"), False, {
        'num_results':     (_('Search results per page'), int,             20),
        'rescan_interval': (_('Misc. data refresh frequency'), int,       900),
        'open_in_browser': p(_('Open in browser on startup'), bool,      True),
        'auto_mark_as_read':(_('Automatically mark e-mail as read'),
                                                                   bool, True),
        'web_content':     (_('Download content from the web'),
                            ["off", "anon", "on"],                  "unknown"),
        'html5_sandbox':   (_('Use HTML5 sandboxes'), bool,              True),
        'attachment_urls': (_('URLs to treat as attachments (regex)'), str, []),
        'weak_crypto_max_age': (
               _('Accept weak crypto in messages older than this (unix time)'),
                                                                  int,      0),
        'encrypted_block_html': (_('Never display HTML from encrypted mail'),
                                                                   bool, True),
        'encrypted_block_web': (_('Never fetch web content from encrypted mail'),
                                                                   bool, True),
        'gpg_use_agent':   (_('Use the local GnuPG agent'), bool,       False),
        'gpg_clearsign':  X(_('Inline PGP signatures or attached'),
                            bool, False),
        'gpg_recipient':   (_('Encrypt local data to ...'), 'gpgkeyid',    ''),
        'gpg_email_key':   (_('Enable e-mail based public key distribution'),
                            bool, True),
        'gpg_html_wrap':   (_('Wrap keys and signatures in helpful HTML'),
                            bool, True),
        'antiphishing':    (_("Enable experimental anti-phishing heuristics "),
                                                                  bool, False),
        'key_tofu':        (_("Key Import Behaviour"), False, {
            'autocrypt':   (_('Auto-import keys using Autocrypt state machine'),
                            bool, True),
            'historic':    (_('Auto-import keys using communication history'),
                            bool, True),
            'hist_min':    (_('Require this many signed- or encrypted e-mails'),
                            int, 3),
            'hist_recent': (_('Consider the most recent N e-mails (per sender)'),
                            int, 6),
            'hist_origins':(_('Origins to auto-import keys from (historic)'),
                            str, 'e-mail, wkd, koo'),
            'min_interval': (_('Interval between TOFU checks (per sender)'),
                            int, 1800),
        }),
        'key_trust':       (_("Key Trust Model"), False, {
            'threshold':    (_('Minimum number of signatures required'),
                             int, 5),
            'window_days':  (_('Window of time (days) to evaluate trust'),
                             int, 180),
            'sig_warn_pct': (_('Signed ratio (%) above which we expect sigs'),
                             int, 80),
            'key_trust_pct':(_('Ratio of key use (%) above which we trust key'),
                             int, 90),
            'key_new_pct':  (_('Consider key new below this ratio (%) of sigs'),
                             int, 10)
        }),
        'openpgp_header': X(_('Advertise PGP preferences in a header?'),
                            ['', 'sign', 'encrypt', 'signencrypt'],
                            'signencrypt'),
        'crypto_policy':  X(_('Default encryption policy for outgoing mail'),
                            str, 'none'),
        'inline_pgp':      (_('Use inline PGP when possible'), bool,     True),
        'encrypt_subject': (_('Encrypt subjects by default'), bool,      True),
        'default_order':   (_('Default sort order'), str,          'rev-date'),
        'obfuscate_index':X(_('Key to use to scramble the index'), str,    ''),
        'index_encrypted':X(_('Make encrypted content searchable'),
                            bool, False),
        'encrypt_mail':   X(_('Encrypt locally stored mail'), bool,     False),
        'encrypt_index':  X(_('Encrypt the local search index'), bool,  False),
        'encrypt_vcards': X(_('Encrypt the contact database'), bool,     True),
        'encrypt_events': X(_('Encrypt the event log'), bool,            True),
        'encrypt_misc':   X(_('Encrypt misc. local data'), bool,         True),
        'allow_deletion': X(_('Allow permanent deletion of e-mails'),
                                                                  bool, False),
        'deletion_ratio': X(_('Max fraction of source mail to delete per pass'),
                                                                 float,  0.75),
# FIXME:
#       'backup_to_web':  X(_('Backup settings and keys to mobile web app'),
#                                                                  bool, True),
#       'backup_to_email':X(_('Backup settings and keys to e-mail'),  str, ''),
        'rescan_command':  (_('Command run before rescanning'), str,       ''),
        'default_email':   (_('Default outgoing e-mail address'), 'email', ''),
        'default_route':   (_('Default outgoing mail route'), str, ''),
        'line_length':     (_('Target line length, <40 disables reflow'),
                            int, 65),
        'always_bcc_self': (_('Always BCC self on outgoing mail'), bool, True),
        'default_messageroute': (_('Default outgoing mail route'), str,    ''),
        'language':       p(_('User interface language'), str,             ''),
        'vcard':           [_("vCard import/export settings"), False, {
            'importers':   [_("vCard import settings"), False,             {}],
            'exporters':   [_("vCard export settings"), False,             {}],
            'context':     [_("vCard context helper settings"), False,     {}],
        }],
        'friendly_pipes':  (_("Enable sh-like pipes in the CLI"), bool,  True),
    }),
    'web': (_("Web Interface Preferences"), False, {
        'keybindings':     (_('Enable keyboard short-cuts'), bool, False),
        'developer_mode':  (_('Enable developer-only features'), bool, False),
        'friendly_dates':  (_('UI uses "friendly" date/times'), bool,    True),
        'setup_complete':  (_('User completed setup experience'), bool, False),
        'display_density': (_('Display density of interface'), str, 'comfy'),
        'quoted_reply':    (_('Quote replies to messages'), str, 'unset'),
        'nag_backup_key':   (_('Nag user to backup their key'), int, 0),
        'subtags_collapsed': (_('Collapsed subtags in sidebar'), str, []),
        'donate_visibility': (_('Display donate link in topbar?'), bool, True),
        'email_html_hint':   (_('Display HTML hints?'), bool, True),
        'email_crypto_hint': (_('Display crypto hints?'), bool, True),
        'email_reply_hint':  (_('Display reply hints?'), bool, True),
        'email_tag_hint':    (_('Display tagging hints?'), bool, True),
        'release_notes':     (_('Display release notes?'), bool, True)
    }),
    'logins': [_('Credentials allowed to access Mailpile'), {
        'password':        (_('Salted and hashed password'), str, '')
    }, {}],
    'secrets': [_('Secrets the user wants saved'), {
        'password':        (_('A secret'), str, ''),
        'policy':          (_('Security policy'),
                            ["store", "cache-only", "fail", "protect"],
                            'store')
    }, {}],
    'tls': [_('Settings for TLS certificate validation'), {
        'server':          (_('Server hostname:port'), str, ''),
        'accept_certs':    (_('SHA256 of acceptable certs'), str, []),
        'use_web_ca':      (_('Use web certificate authorities'), bool, True)
    }, {}],
    'routes': [_('Outgoing message routes'), {
        'name':            (_('Route name'), str, ''),
        'protocol':        (_('Messaging protocol'),
                            ["smtp", "smtptls", "smtpssl", "local"],
                            'smtp'),
        'username':        (_('User name'), str, ''),
        'password':        (_('Password'), str, ''),
        'auth_type':       (_('Authentication scheme'), str, 'password-cleartext'),
        'command':         (_('Shell command'), str, ''),
        'host':            (_('Host'), str, ''),
        'port':            (_('Port'), int, 587)
    }, {}],
    'sources': [_('Incoming message sources'), {
        'name':            (_('Source name'), str, ''),
        'profile':         (_('Profile this source belongs to'), str, ''),
        'enabled':         (_('Is this mail source enabled?'), bool, True),
        'protocol':        (_('Mail source protocol'),
                            ["local",
                             "imap", "imap_ssl", "imap_tls",
                             "pop3", "pop3_ssl",
                             # These are all obsolete, handled as local:
                             "mbox", "maildir", "macmaildir", "gmvault"],
                            ''),
        'pre_command':     (_('Shell command run before syncing'), str, ''),
        'post_command':    (_('Shell command run after syncing'), str, ''),
        'interval':        (_('How frequently to check for mail'), int, 300),
        'username':        (_('User name'), str, ''),
        'password':        (_('Password'), str, ''),
        'auth_type':       (_('Authentication scheme'), str, 'password'),
        'host':            (_('Host'), str, ''),
        'port':            (_('Port'), int, 993),
        'keepalive':       (_('Keep server connections alive'), bool, False),
        'discovery':       (_('Mailbox discovery policy'), False, {
            'paths':       (_('Paths to watch for new mailboxes'), 'bin', []),
            'policy':      (_('Default mailbox policy'),
                            ['unknown', 'ignore', 'watch',
                             'read', 'move', 'sync'], 'unknown'),
            'local_copy':  (_('Copy mail to a local mailbox?'), bool, False),
            'parent_tag':  (_('Parent tag for mailbox tags'), str, '!CREATE'),
            'guess_tags':  (_('Guess which local tags match'), bool, True),
            'create_tag':  (_('Create a tag for each mailbox?'), bool, True),
            'visible_tags':(_('Make tags visible by default?'), bool, True),
            'process_new': (_('Is a potential source of new mail'), bool, True),
            'apply_tags':  (_('Tags applied to messages'), str, []),
            'max_mailboxes':(_('Max mailboxes to add'), int, 100),
        }),
        'mailbox': (_('Mailboxes'), {
            'name':        (_('The name of this mailbox'), str, ''),
            'path':        (_('Mailbox source path'), str, ''),
            'policy':      (_('Mailbox policy'),
                            ['unknown', 'ignore', 'read', 'move', 'sync',
                             'inherit'], 'inherit'),
            'local':       (_('Local mailbox path'), 'bin', ''),
            'process_new': (_('Is a source of new mail'), bool, True),
            'primary_tag': (_('A tag representing this mailbox'), str, ''),
            'apply_tags':  (_('Tags applied to messages'), str, []),
        }, {})
    }, {}]
}


if __name__ == "__main__":
    import mailpile.config.defaults
    from mailpile.config.base import ConfigDict

    print('%s' % (ConfigDict(_name='mailpile',
                             _comment='Base configuration',
                             _rules=mailpile.config.defaults.CONFIG_RULES
                             ).as_config_bytes(), ))


================================================
FILE: mailpile/config/detect.py
================================================
try:
    import ssl
except ImportError:
    ssl = None

try:
    import sockschain as socks
except ImportError:
    try:
        import socks
    except ImportError:
        socks = None


================================================
FILE: mailpile/config/manager.py
================================================
from __future__ import print_function
import copy
import cPickle
import io
import jinja2
import json
import os
import socket
import sys
import random
import re
import threading
import fasteners
import traceback
import ConfigParser
import errno

from urllib import quote, unquote, getproxies
from urlparse import urlparse

try:
    from appdirs import AppDirs
except ImportError:
    AppDirs = None

import mailpile.platforms
from mailpile.command_cache import CommandCache
from mailpile.crypto.streamer import DecryptingStreamer
from mailpile.crypto.gpgi import GnuPG
from mailpile.eventlog import EventLog, Event, GetThreadEvent
from mailpile.httpd import HttpWorker
from mailpile.i18n import gettext as _
from mailpile.i18n import ngettext as _n
from mailpile.mailboxes import OpenMailbox, NoSuchMailboxError, wervd
from mailpile.mailutils import FormatMbxId, MBX_ID_LEN
from mailpile.search import MailIndex
from mailpile.search_history import SearchHistory
from mailpile.security import SecurePassphraseStorage
from mailpile.ui import Session, BackgroundInteraction
from mailpile.util import *
from mailp
Download .txt
gitextract_dkjlc6xq/

├── .coveragerc
├── .dockerignore
├── .gitignore
├── .gitmodules
├── .travis.yml
├── .tx/
│   └── config
├── AGPLv3.txt
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── COPYING.md
├── DEV_FAQ.md
├── Dockerfile
├── Dockerfile.dev
├── Gruntfile.js
├── MANIFEST.in
├── Makefile
├── README.md
├── babel.cfg
├── bower.json
├── docker-compose.dev.yml
├── docker-compose.yml
├── install_hooks.py
├── mailpile/
│   ├── __init__.py
│   ├── __main__.py
│   ├── app.py
│   ├── auth.py
│   ├── command_cache.py
│   ├── commands.py
│   ├── config/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── defaults.py
│   │   ├── detect.py
│   │   ├── manager.py
│   │   ├── paths.py
│   │   └── validators.py
│   ├── conn_brokers.py
│   ├── crypto/
│   │   ├── __init__.py
│   │   ├── aes_utils.py
│   │   ├── autocrypt.py
│   │   ├── gpgi.py
│   │   ├── keyinfo.py
│   │   ├── mime.py
│   │   ├── records.py
│   │   ├── state.py
│   │   ├── streamer.py
│   │   └── tor.py
│   ├── eventlog.py
│   ├── httpd.py
│   ├── i18n.py
│   ├── index/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── mailboxes.py
│   │   ├── msginfo.py
│   │   └── search.py
│   ├── mail_source/
│   │   ├── __init__.py
│   │   ├── imap.py
│   │   ├── imap_starttls.py
│   │   ├── imap_utf7.py
│   │   ├── local.py
│   │   └── pop3.py
│   ├── mailboxes/
│   │   ├── __init__.py
│   │   ├── gmvault.py
│   │   ├── macmail.py
│   │   ├── maildir.py
│   │   ├── maildirwin.py
│   │   ├── mbox.py
│   │   ├── pop3.py
│   │   └── wervd.py
│   ├── mailutils/
│   │   ├── __init__.py
│   │   ├── addresses.py
│   │   ├── emails.py
│   │   ├── generator.py
│   │   ├── header.py
│   │   ├── headerprint.py
│   │   ├── html.py
│   │   ├── safe.py
│   │   └── vcal.py
│   ├── packing.py
│   ├── platforms.py
│   ├── plugins/
│   │   ├── __init__.py
│   │   ├── autotag.py
│   │   ├── autotag_sb.py
│   │   ├── backups.py
│   │   ├── compose.py
│   │   ├── contacts.py
│   │   ├── core.py
│   │   ├── crypto_autocrypt.py
│   │   ├── crypto_gnupg.py
│   │   ├── crypto_policy.py
│   │   ├── cryptostate.py
│   │   ├── dates.py
│   │   ├── eventlog.py
│   │   ├── exporters.py
│   │   ├── groups.py
│   │   ├── gui.py
│   │   ├── html_magic.py
│   │   ├── keylookup/
│   │   │   ├── __init__.py
│   │   │   ├── email_keylookup.py
│   │   │   └── wkd.py
│   │   ├── migrate.py
│   │   ├── motd.py
│   │   ├── oauth.py
│   │   ├── plugins.py
│   │   ├── search.py
│   │   ├── setup_magic.py
│   │   ├── setup_magic_ispdb.py
│   │   ├── sizes.py
│   │   ├── smtp_server.py
│   │   ├── tags.py
│   │   ├── vcard_carddav.py
│   │   ├── vcard_gnupg.py
│   │   ├── vcard_gravatar.py
│   │   ├── vcard_libravatar.py
│   │   ├── vcard_mork.py
│   │   └── webterminal.py
│   ├── postinglist.py
│   ├── safe_popen.py
│   ├── search.py
│   ├── search_history.py
│   ├── security.py
│   ├── smtp_client.py
│   ├── spambayes/
│   │   ├── LICENSE.txt
│   │   ├── Options.py
│   │   ├── OptionsClass.py
│   │   ├── Tester.py
│   │   ├── __init__.py
│   │   ├── chi2.py
│   │   ├── classifier.py
│   │   └── safepickle.py
│   ├── tests/
│   │   ├── TESTS
│   │   ├── __init__.py
│   │   ├── data/
│   │   │   ├── Maildir/
│   │   │   │   ├── cur/
│   │   │   │   │   ├── .gitkeep
│   │   │   │   │   ├── 1379857166.25979_1.hottie,2,S
│   │   │   │   │   ├── 1379857166.25979_3.hottie,2,S
│   │   │   │   │   ├── 1379857166.25979_5.hottie,2,S
│   │   │   │   │   ├── 1379857166.25979_7.hottie,2,S
│   │   │   │   │   ├── 1379857166.25979_9.hottie,2,S
│   │   │   │   │   ├── 1379857166.25980_9.hottie,2,S
│   │   │   │   │   ├── 1379857166.25981_10.hottie,2,S
│   │   │   │   │   ├── broken_email_1396694612
│   │   │   │   │   ├── encrypted-empty-content-w-key.eml
│   │   │   │   │   ├── fw-question-1443197278.mbx
│   │   │   │   │   ├── mailpile-1398950855.mbx
│   │   │   │   │   ├── mailpile-1400069992.mbx
│   │   │   │   │   └── no-subject-1400067892.mbx
│   │   │   │   ├── new/
│   │   │   │   │   └── .gitkeep
│   │   │   │   └── tmp/
│   │   │   │       └── .gitkeep
│   │   │   ├── contacts/
│   │   │   │   ├── README
│   │   │   │   ├── contacttest.ldif
│   │   │   │   ├── contacttest.mork
│   │   │   │   └── contacttest.vcf
│   │   │   ├── gpg-keyring/
│   │   │   │   ├── pubring.gpg
│   │   │   │   ├── secring.gpg
│   │   │   │   ├── testing.gpg.pub
│   │   │   │   └── trustdb.gpg
│   │   │   ├── pgp-data/
│   │   │   │   ├── buildexamples.py
│   │   │   │   └── sources/
│   │   │   │       ├── ar.iso-8859-6
│   │   │   │       ├── ar.utf-8
│   │   │   │       ├── ar.windows-1256
│   │   │   │       ├── cn.utf-8
│   │   │   │       ├── gr.iso-8859-7
│   │   │   │       ├── gr.utf-8
│   │   │   │       ├── gr.windows-1253
│   │   │   │       ├── ru.koi8-ru
│   │   │   │       ├── ru.utf-8
│   │   │   │       ├── ru.windows-1251
│   │   │   │       └── vi.utf-8
│   │   │   ├── pub.key
│   │   │   └── tests.mbx
│   │   ├── gui/
│   │   │   ├── __init__.py
│   │   │   ├── test_contacts.py
│   │   │   ├── test_mail.py
│   │   │   └── test_tags.py
│   │   ├── test_command.py
│   │   ├── test_config.py
│   │   ├── test_crypto_policy.py
│   │   ├── test_eventlog.py
│   │   ├── test_keylookup.py
│   │   ├── test_mail_generator.py
│   │   ├── test_mailutils.py
│   │   ├── test_performance.py
│   │   ├── test_plugin.py
│   │   ├── test_search.py
│   │   ├── test_ui.py
│   │   ├── test_vcard.py
│   │   └── test_vcard_mork.py
│   ├── ui.py
│   ├── urlmap.py
│   ├── util.py
│   ├── vcard.py
│   ├── vfs.py
│   ├── workers.py
│   └── www/
│       ├── __init__.py
│       ├── jinjaextensions.py
│       └── jinjaloader.py
├── mp.cmd
├── package.json
├── packages/
│   ├── Dockerfile_debian
│   ├── debian/
│   │   ├── changelog
│   │   ├── compat
│   │   ├── control
│   │   ├── copyright
│   │   ├── gbp.conf
│   │   ├── mailpile-apache2.dirs
│   │   ├── mailpile-apache2.install
│   │   ├── mailpile-apache2.links
│   │   ├── mailpile-apache2.lintian-overrides
│   │   ├── mailpile-apache2.postinst
│   │   ├── mailpile-apache2.postrm
│   │   ├── mailpile-desktop.install
│   │   ├── mailpile-desktop.links
│   │   ├── mailpile.install
│   │   ├── mailpile.lintian-overrides
│   │   ├── mailpile.manpages
│   │   ├── rules
│   │   ├── source/
│   │   │   └── format
│   │   └── watch
│   ├── docker/
│   │   └── entrypoint.sh
│   ├── macos/
│   │   ├── README.md
│   │   ├── appdmg.json.template
│   │   ├── brew/
│   │   │   └── symlinks.rb
│   │   ├── build-script/
│   │   │   ├── 00-check-dependencies
│   │   │   ├── 10-create-app-icons
│   │   │   ├── 11-build-gui-o-mac-tic
│   │   │   ├── 20-build-homebrew
│   │   │   ├── 21-install-mailpile-deps
│   │   │   ├── 30-slim-down
│   │   │   ├── 55-install-mailpile
│   │   │   ├── 90-fix-libraries
│   │   │   ├── 91-fix-paths
│   │   │   ├── 92-fix-python-launcher
│   │   │   ├── 98-codesign
│   │   │   └── 99-make-dmg
│   │   ├── build.sh
│   │   ├── configurator.sh
│   │   ├── dmgbuild-settings.py
│   │   └── mailpile
│   ├── mailpile.1
│   ├── redhat/
│   │   └── mailpile.spec.in
│   ├── scripts/
│   │   ├── build-controller.py
│   │   ├── build-deb.sh
│   │   ├── build-desktop.py
│   │   ├── crontab
│   │   ├── deb-package-py-deps.sh
│   │   ├── kite-runner-mac.cfg
│   │   ├── kite-runner-sample.cfg
│   │   ├── kite-runner-win.cfg
│   │   ├── kite-runner.py
│   │   ├── kiterunner.service.plist
│   │   └── update-repos.sh
│   └── windows-wix/
│       ├── README.QUICK_START.md
│       ├── README.md
│       ├── TODO.md
│       ├── assets/
│       │   └── LicenseText.rtf
│       ├── bin/
│       │   ├── launch-mailpile.bat
│       │   └── with-mailpile-env.py
│       ├── dependencies-windows.txt
│       ├── package.json
│       ├── package.py
│       ├── package_template.json
│       ├── provide/
│       │   ├── __init__.py
│       │   ├── __main__.py
│       │   ├── build.py
│       │   ├── cache.py
│       │   ├── default.py
│       │   └── scripts/
│       │       ├── __init__.py
│       │       ├── bootstrap.py
│       │       ├── export.py
│       │       ├── external.py
│       │       ├── extract_msi.py
│       │       ├── git_checkout.py
│       │       ├── msi_package.py
│       │       ├── python27.py
│       │       ├── root.py
│       │       ├── sign_tree.py
│       │       ├── util.py
│       │       ├── version.py
│       │       ├── win4gpg.py
│       │       └── zip.py
│       ├── provide-example-bootstrap-local.json
│       ├── provide-example-bootstrap-remote.json
│       ├── provide-example-fork-local.json
│       ├── provide-example-fork-remote.json
│       ├── provide.json
│       └── resources.json
├── requirements-dev.txt
├── requirements-with-deps.txt
├── requirements.txt
├── scripts/
│   ├── __init__.py
│   ├── add-gpgme-and-gnupg-to-venv
│   ├── clear-cache.sh
│   ├── colorprints.py
│   ├── compile-messages.sh
│   ├── create-debian-changelog.py
│   ├── docker-dev/
│   │   ├── down
│   │   ├── shell
│   │   └── up
│   ├── email-parsing-test.py
│   ├── gitwhere.sh
│   ├── gpg
│   ├── less-compiler.in
│   ├── mailpile
│   ├── mailpile-admin
│   ├── mailpile-decrypt.py
│   ├── mailpile-pyside.py
│   ├── mailpile-test.py
│   ├── make-messages.sh
│   ├── mbox-minimal.py
│   ├── minimize-pgp-key.py
│   ├── mk-credits.py
│   ├── nginx.conf
│   ├── python-lint.sh
│   ├── reset-mac-mailpile.sh
│   ├── setup-test.sh
│   ├── test-sendmail.sh
│   ├── unsent-mail-finder.py
│   └── version.py
├── setup.cfg
├── setup.py
├── shared-data/
│   ├── contrib/
│   │   ├── README.md
│   │   ├── autoajax/
│   │   │   ├── autoajax.js
│   │   │   └── manifest.json
│   │   ├── datadig/
│   │   │   ├── datadig-modal.html
│   │   │   ├── datadig.html
│   │   │   ├── datadig.js
│   │   │   ├── datadig.py
│   │   │   └── manifest.json
│   │   ├── demos/
│   │   │   ├── demos.css
│   │   │   ├── demos.js
│   │   │   ├── demos.py
│   │   │   ├── manifest.json
│   │   │   ├── md5sum-wordy.html
│   │   │   ├── md5sum.html
│   │   │   ├── md5sum.xml
│   │   │   └── search.html
│   │   ├── experiments/
│   │   │   ├── experiments.py
│   │   │   └── manifest.json
│   │   ├── forcegrapher/
│   │   │   ├── README.md
│   │   │   ├── d3.drasl.js
│   │   │   ├── d3.js
│   │   │   ├── forcegrapher.css
│   │   │   ├── forcegrapher.html
│   │   │   ├── forcegrapher.js
│   │   │   ├── forcegrapher.py
│   │   │   └── manifest.json
│   │   ├── hacks/
│   │   │   ├── hacks.py
│   │   │   └── manifest.json
│   │   ├── hints/
│   │   │   ├── hints/
│   │   │   │   ├── autotagging.html
│   │   │   │   ├── backups.html
│   │   │   │   ├── deletion.html
│   │   │   │   ├── dragging-tags.html
│   │   │   │   ├── gravatar.html
│   │   │   │   ├── keyboard-shortcuts.html
│   │   │   │   ├── organize-sidebar.html
│   │   │   │   └── spam.html
│   │   │   ├── hints.html
│   │   │   ├── hints.js
│   │   │   ├── hints.py
│   │   │   └── manifest.json
│   │   ├── i18nhelper/
│   │   │   ├── i18nhelper.js
│   │   │   ├── i18nhelper.py
│   │   │   └── manifest.json
│   │   ├── maildeck/
│   │   │   ├── maildeck.css
│   │   │   ├── maildeck.html
│   │   │   ├── maildeck.js
│   │   │   ├── maildeck.py
│   │   │   └── manifest.json
│   │   ├── remoteaccess/
│   │   │   ├── manifest.json
│   │   │   └── settings-remote.html
│   │   └── unthread/
│   │       ├── i18n-stubs.html
│   │       ├── manifest.json
│   │       ├── modal.html
│   │       └── unthread.js
│   ├── default-theme/
│   │   ├── README.md
│   │   ├── css/
│   │   │   ├── default.css
│   │   │   ├── guide.css
│   │   │   └── print.css
│   │   ├── html/
│   │   │   ├── abortabortabort/
│   │   │   │   └── index.html
│   │   │   ├── auth/
│   │   │   │   ├── login/
│   │   │   │   │   └── index.html
│   │   │   │   ├── logout/
│   │   │   │   │   └── index.html
│   │   │   │   └── shared.html
│   │   │   ├── backup/
│   │   │   │   └── restore/
│   │   │   │       └── index.html
│   │   │   ├── browse/
│   │   │   │   └── index.html
│   │   │   ├── contacts/
│   │   │   │   ├── add/
│   │   │   │   │   └── index.html
│   │   │   │   ├── import/
│   │   │   │   │   └── index.html
│   │   │   │   ├── index.html
│   │   │   │   └── view/
│   │   │   │       └── index.html
│   │   │   ├── crypto/
│   │   │   │   ├── gpg/
│   │   │   │   │   └── key/
│   │   │   │   │       └── index.html
│   │   │   │   └── tls/
│   │   │   │       └── getcert/
│   │   │   │           └── index.html
│   │   │   ├── filter/
│   │   │   │   └── list/
│   │   │   │       └── index.html
│   │   │   ├── group/
│   │   │   │   ├── add/
│   │   │   │   │   └── index.html
│   │   │   │   └── index.html
│   │   │   ├── help/
│   │   │   │   ├── bottom.html
│   │   │   │   └── index.html
│   │   │   ├── jsapi/
│   │   │   │   ├── app.js
│   │   │   │   ├── compose/
│   │   │   │   │   ├── attachments.js
│   │   │   │   │   ├── autosave.js
│   │   │   │   │   ├── body.js
│   │   │   │   │   ├── complete.js
│   │   │   │   │   ├── crypto.js
│   │   │   │   │   ├── events.js
│   │   │   │   │   ├── init.js
│   │   │   │   │   ├── modals.js
│   │   │   │   │   ├── recipients.js
│   │   │   │   │   └── tooltips.js
│   │   │   │   ├── contacts/
│   │   │   │   │   ├── display_modes.js
│   │   │   │   │   ├── events.js
│   │   │   │   │   ├── init.js
│   │   │   │   │   └── modals.js
│   │   │   │   ├── crypto/
│   │   │   │   │   ├── events.js
│   │   │   │   │   ├── find.js
│   │   │   │   │   ├── import.js
│   │   │   │   │   ├── init.js
│   │   │   │   │   ├── modals.js
│   │   │   │   │   ├── tooltips.js
│   │   │   │   │   └── ui.js
│   │   │   │   ├── global/
│   │   │   │   │   ├── activities.js
│   │   │   │   │   ├── eventlog.js
│   │   │   │   │   ├── global.js
│   │   │   │   │   ├── helpers.js
│   │   │   │   │   └── silly.js
│   │   │   │   ├── index.css
│   │   │   │   ├── index.html
│   │   │   │   ├── index.js
│   │   │   │   ├── index.txt
│   │   │   │   ├── message/
│   │   │   │   │   ├── events.js
│   │   │   │   │   ├── html-sandbox.js
│   │   │   │   │   ├── init.js
│   │   │   │   │   ├── message.js
│   │   │   │   │   ├── tooltips.js
│   │   │   │   │   └── ui.js
│   │   │   │   ├── pwa/
│   │   │   │   │   ├── manifest.json
│   │   │   │   │   └── service-worker.js
│   │   │   │   ├── search/
│   │   │   │   │   ├── bulk_actions.js
│   │   │   │   │   ├── display_modes.js
│   │   │   │   │   ├── events.js
│   │   │   │   │   ├── init.js
│   │   │   │   │   ├── selection_actions.js
│   │   │   │   │   ├── tooltips.js
│   │   │   │   │   └── ui.js
│   │   │   │   ├── settings/
│   │   │   │   │   └── content.js
│   │   │   │   ├── tags/
│   │   │   │   │   ├── events.js
│   │   │   │   │   ├── init.js
│   │   │   │   │   └── modals.js
│   │   │   │   ├── templates/
│   │   │   │   │   ├── modal-add-tag.html
│   │   │   │   │   ├── modal-auto.html
│   │   │   │   │   ├── modal-compose-quoted-reply.html
│   │   │   │   │   ├── modal-composer-encryption-helper.html
│   │   │   │   │   ├── modal-contact-add.html
│   │   │   │   │   ├── modal-display-keybindings.html
│   │   │   │   │   ├── modal-related-search.html
│   │   │   │   │   ├── modal-save-search.html
│   │   │   │   │   ├── modal-search-keyservers.html
│   │   │   │   │   ├── modal-send-public-key.html
│   │   │   │   │   ├── modal-tag-picker.html
│   │   │   │   │   ├── modal-upload-key.html
│   │   │   │   │   └── template-modal-tag-picker-item.html
│   │   │   │   └── ui/
│   │   │   │       ├── content.js
│   │   │   │       ├── events.js
│   │   │   │       ├── global.js
│   │   │   │       ├── init.js
│   │   │   │       ├── keybindings.js
│   │   │   │       ├── notifications.js
│   │   │   │       ├── selection.js
│   │   │   │       ├── sidebar.js
│   │   │   │       ├── tagging.js
│   │   │   │       ├── terminal.js
│   │   │   │       ├── tooltips.js
│   │   │   │       └── topbar.js
│   │   │   ├── layouts/
│   │   │   │   ├── auth.html
│   │   │   │   ├── content-tall.html
│   │   │   │   ├── content-wide.html
│   │   │   │   ├── content.html
│   │   │   │   ├── full-tall.html
│   │   │   │   ├── full-wide.html
│   │   │   │   ├── full.html
│   │   │   │   ├── minimal-tall.html
│   │   │   │   ├── minimal-wide.html
│   │   │   │   └── minimal.html
│   │   │   ├── logs/
│   │   │   │   ├── events/
│   │   │   │   │   └── index.html
│   │   │   │   ├── layout.html
│   │   │   │   └── network/
│   │   │   │       └── index.html
│   │   │   ├── message/
│   │   │   │   ├── compose/
│   │   │   │   │   └── index.html
│   │   │   │   ├── download/
│   │   │   │   │   └── index.html
│   │   │   │   ├── draft/
│   │   │   │   │   └── index.html
│   │   │   │   ├── forward/
│   │   │   │   │   └── forward.html
│   │   │   │   ├── index.html
│   │   │   │   ├── reply/
│   │   │   │   │   ├── composer.html
│   │   │   │   │   └── index.html
│   │   │   │   ├── send/
│   │   │   │   │   └── index.html
│   │   │   │   ├── single.html
│   │   │   │   └── update/
│   │   │   │       └── index.html
│   │   │   ├── page/
│   │   │   │   ├── contribute/
│   │   │   │   │   ├── bugs.html
│   │   │   │   │   └── index.html
│   │   │   │   ├── entropy/
│   │   │   │   │   └── index.html
│   │   │   │   ├── gmail-2-step-verification/
│   │   │   │   │   └── index.html
│   │   │   │   ├── gmail-access-non-google-accounts/
│   │   │   │   │   └── index.html
│   │   │   │   ├── index.html
│   │   │   │   ├── multipile/
│   │   │   │   │   └── index.html
│   │   │   │   ├── release-notes/
│   │   │   │   │   ├── credits-code.html
│   │   │   │   │   ├── credits-i18n.html
│   │   │   │   │   ├── credits.html
│   │   │   │   │   ├── index.html
│   │   │   │   │   └── license.html
│   │   │   │   └── template-straw-man/
│   │   │   │       ├── index.html
│   │   │   │       ├── index.js
│   │   │   │       └── tags.html
│   │   │   ├── partials/
│   │   │   │   ├── compose.html
│   │   │   │   ├── error_message_missing.html
│   │   │   │   ├── errors_content.html
│   │   │   │   ├── head.html
│   │   │   │   ├── helpers.html
│   │   │   │   ├── hidden.html
│   │   │   │   ├── javascript.html
│   │   │   │   ├── modals.html
│   │   │   │   ├── pile_compose.html
│   │   │   │   ├── pile_email_hints.html
│   │   │   │   ├── pile_email_tags.html
│   │   │   │   ├── pile_message.html
│   │   │   │   ├── pile_threading.html
│   │   │   │   ├── search_item.html
│   │   │   │   ├── sidebar.html
│   │   │   │   ├── tag_add.html
│   │   │   │   ├── thread_message_cryptofail.html
│   │   │   │   ├── tools_contacts.html
│   │   │   │   ├── tools_default.html
│   │   │   │   ├── tools_message.html
│   │   │   │   ├── tools_search.html
│   │   │   │   ├── tools_settings.html
│   │   │   │   ├── tools_tags.html
│   │   │   │   ├── tooltips.html
│   │   │   │   └── topbar.html
│   │   │   ├── plugins/
│   │   │   │   └── index.html
│   │   │   ├── profiles/
│   │   │   │   ├── account-form.html
│   │   │   │   ├── add/
│   │   │   │   │   └── index.html
│   │   │   │   ├── edit/
│   │   │   │   │   └── index.html
│   │   │   │   ├── index.html
│   │   │   │   └── remove/
│   │   │   │       └── index.html
│   │   │   ├── quitquitquit/
│   │   │   │   └── index.html
│   │   │   ├── search/
│   │   │   │   ├── atts.html
│   │   │   │   ├── default.html
│   │   │   │   ├── drafts.html
│   │   │   │   ├── index.html
│   │   │   │   ├── outbox.html
│   │   │   │   ├── outgoing.html
│   │   │   │   ├── photos.html
│   │   │   │   ├── prev_more_next.html
│   │   │   │   ├── sent.html
│   │   │   │   └── trash.html
│   │   │   ├── settings/
│   │   │   │   ├── index.html
│   │   │   │   ├── mailbox/
│   │   │   │   │   └── index.html
│   │   │   │   ├── plugins.html
│   │   │   │   ├── preferences.html
│   │   │   │   ├── privacy.html
│   │   │   │   ├── recipes.html
│   │   │   │   └── set/
│   │   │   │       ├── index.html
│   │   │   │       └── password/
│   │   │   │           ├── index.html
│   │   │   │           └── keys.html
│   │   │   ├── setup/
│   │   │   │   ├── oauth2/
│   │   │   │   │   └── index.html
│   │   │   │   ├── password/
│   │   │   │   │   └── index.html
│   │   │   │   └── welcome/
│   │   │   │       └── index.html
│   │   │   └── tags/
│   │   │       ├── add/
│   │   │       │   └── index.html
│   │   │       ├── edit.html
│   │   │       ├── form.html
│   │   │       ├── index.html
│   │   │       └── sidebar.html
│   │   ├── index.html
│   │   ├── js/
│   │   │   ├── helpers.js
│   │   │   ├── libraries.js
│   │   │   └── mousetrap.global.bind.js
│   │   ├── less/
│   │   │   ├── app/
│   │   │   │   ├── attachments.less
│   │   │   │   ├── compose.less
│   │   │   │   ├── contacts.less
│   │   │   │   ├── crypto.less
│   │   │   │   ├── files.less
│   │   │   │   ├── global.less
│   │   │   │   ├── helpers.less
│   │   │   │   ├── icons.less
│   │   │   │   ├── library-override.less
│   │   │   │   ├── login.less
│   │   │   │   ├── message.less
│   │   │   │   ├── mobile.less
│   │   │   │   ├── modals.less
│   │   │   │   ├── navigation.less
│   │   │   │   ├── notifications.less
│   │   │   │   ├── pile.less
│   │   │   │   ├── profiles.less
│   │   │   │   ├── screens.less
│   │   │   │   ├── search.less
│   │   │   │   ├── settings.less
│   │   │   │   ├── setup.less
│   │   │   │   ├── sidebar.less
│   │   │   │   ├── tablet.less
│   │   │   │   ├── tags.less
│   │   │   │   ├── terminal.less
│   │   │   │   ├── thread.less
│   │   │   │   ├── tooltips.less
│   │   │   │   ├── topbar.less
│   │   │   │   └── webfonts.less
│   │   │   ├── config.less
│   │   │   ├── default.less
│   │   │   ├── libraries/
│   │   │   │   ├── bootstrap.less
│   │   │   │   ├── dropdowns.less
│   │   │   │   ├── modals.less
│   │   │   │   └── typeahead.less
│   │   │   └── print.less
│   │   ├── theme.json
│   │   └── webfonts/
│   │       ├── LICENSE
│   │       └── index.html
│   ├── locale/
│   │   ├── README.md
│   │   └── mailpile.pot
│   ├── mailpile-gui/
│   │   ├── icons-osx/
│   │   │   └── mk_icons.sh
│   │   ├── mailpile-gui.py
│   │   ├── mailpile.desktop
│   │   └── media/
│   │       └── background.xcf
│   └── multipile/
│       ├── README.md
│       ├── mailpile-admin.py
│       ├── mailpile-launcher.py
│       ├── multipile.rc.sample
│       └── www/
│           ├── apache-broken.html
│           ├── index.html
│           └── not-running.html
├── test-requirements.txt
└── tox.ini
Download .txt
Showing preview only (335K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (4413 symbols across 177 files)

FILE: install_hooks.py
  function symlink_develop (line 4) | def symlink_develop(config):

FILE: mailpile/__init__.py
  class Mailpile (line 9) | class Mailpile(object):
    method __init__ (line 12) | def __init__(self,
    method _mk_action (line 39) | def _mk_action(self, cls, cmd, argspec):
    method Interact (line 55) | def Interact(self):

FILE: mailpile/__main__.py
  function main (line 5) | def main():

FILE: mailpile/app.py
  function threaded_raw_input (line 41) | def threaded_raw_input(prompt):
  function write_readline_history (line 60) | def write_readline_history(session):
  function CatchUnixSignals (line 70) | def CatchUnixSignals(session):
  function FriendlyPipeTransform (line 89) | def FriendlyPipeTransform(session, opt):
  function Interact (line 108) | def Interact(session):
  class InteractCommand (line 180) | class InteractCommand(Command):
    method command (line 186) | def command(self):
  class WaitCommand (line 216) | class WaitCommand(Command):
    method command (line 222) | def command(self):
  function Main (line 230) | def Main(args):

FILE: mailpile/auth.py
  class UserSession (line 16) | class UserSession(object):
    method __init__ (line 19) | def __init__(self, ts=None, auth=None, data=None):
    method is_expired (line 24) | def is_expired(self, now=None):
    method update_ts (line 27) | def update_ts(self):
  class UserSessionCache (line 31) | class UserSessionCache(dict):
    method delete_expired (line 32) | def delete_expired(self, now=None):
  function VerifyAndStorePassphrase (line 39) | def VerifyAndStorePassphrase(config, passphrase=None, sps=None,
  function SetLoggedIn (line 56) | def SetLoggedIn(cmd, user=None, redirect=False, session_id=None):
  function CheckPassword (line 74) | def CheckPassword(config, username, password):
  function IndirectPassword (line 81) | def IndirectPassword(config, pwd):
  function LogoutAll (line 95) | def LogoutAll():
  class Authenticate (line 100) | class Authenticate(Command):
    method RedirectBack (line 117) | def RedirectBack(cls, url, data):
    method _result (line 124) | def _result(self, result=None):
    method _error (line 131) | def _error(self, message, info=None, result=None):
    method _success (line 138) | def _success(self, message, result=None):
    method _do_redirect (line 141) | def _do_redirect(self):
    method _do_login (line 157) | def _do_login(self, user, password, load_index=False, redirect=False):
    method command (line 211) | def command(self):
  class DeAuthenticate (line 233) | class DeAuthenticate(Command):
    method command (line 243) | def command(self):
  class SetPassphrase (line 262) | class SetPassphrase(Command):
    method _get_profiles (line 287) | def _get_profiles(self):
    method _get_policy (line 290) | def _get_policy(self, fingerprint):
    method _massage_key_info (line 297) | def _massage_key_info(self, fingerprint, key_info, profiles=None, is_l...
    method _lookup_key (line 322) | def _lookup_key(self, keyid, **kwargs):
    method _list_keys (line 330) | def _list_keys(self, **kwargs):
    method _get_account (line 338) | def _get_account(self, cfg):
    method _user_fingerprint (line 344) | def _user_fingerprint(self, username):
    method _list_accounts (line 347) | def _list_accounts(self, only=None):
    method _account_details (line 372) | def _account_details(self, account):
    method _check_master_password (line 375) | def _check_master_password(self, password, account=None, fingerprint=N...
    method _check_password (line 378) | def _check_password(self, password, account=None, fingerprint=None):
    method _prepare_result (line 390) | def _prepare_result(self, account=None, keyid=None, is_locked=False):
    method command (line 404) | def command(self):

FILE: mailpile/command_cache.py
  class CommandCache (line 16) | class CommandCache(object):
    method __init__ (line 39) | def __init__(self, debug=None):
    method cache_result (line 47) | def cache_result(self, fprint, expires, req, cmd_obj, result_obj):
    method get_result (line 61) | def get_result(self, fprint, dirty_check=True, extend=300):
    method dirty_set (line 78) | def dirty_set(self, after=0):
    method mark_dirty (line 86) | def mark_dirty(self, requirements):
    method refresh (line 91) | def refresh(self, extend=0, runtime=5, event_log=None):
  class Cached (line 150) | class Cached(Command):
    method max_age (line 158) | def max_age(self):
    method run (line 164) | def run(self):

FILE: mailpile/commands.py
  class Command (line 27) | class Command(object):
    class CommandResult (line 66) | class CommandResult:
      method __init__ (line 67) | def __init__(self, command_obj, session,
      method __nonzero__ (line 95) | def __nonzero__(self):
      method as_ (line 98) | def as_(self, what, *args, **kwargs):
      method as_text (line 107) | def as_text(self):
      method as_dict (line 127) | def as_dict(self):
      method as_csv (line 158) | def as_csv(self, template=None, result=None):
      method as_json (line 171) | def as_json(self):
      method as_html (line 174) | def as_html(self, template=None):
      method as_js (line 177) | def as_js(self, template=None):
      method as_css (line 180) | def as_css(self, template=None):
      method as_rss (line 183) | def as_rss(self, template=None):
      method as_xml (line 186) | def as_xml(self, template=None):
      method as_txt (line 189) | def as_txt(self, template=None):
      method as_template (line 192) | def as_template(self, ttype,
    method __init__ (line 217) | def __init__(self, session, name=None, arg=None, data=None, async=False):
    method state_as_query_args (line 244) | def state_as_query_args(self):
    method cache_id (line 251) | def cache_id(self, sqa=None):
    method cache_requirements (line 262) | def cache_requirements(self, result):
    method cache_result (line 266) | def cache_result(self, result):
    method template_path (line 281) | def template_path(self, ttype, template_id=None, template=None):
    method _gnupg (line 296) | def _gnupg(self, **kwargs):
    method _config (line 299) | def _config(self):
    method _idx (line 309) | def _idx(self, reset=False, wait=True, wait_all=True, quiet=False):
    method _background_save (line 346) | def _background_save(self,
    method _choose_messages (line 371) | def _choose_messages(self, words, allow_ephemeral=False):
    method _error (line 418) | def _error(self, message, info=None, result=None):
    method _success (line 435) | def _success(self, message, result=True):
    method _read_file_or_data (line 444) | def _read_file_or_data(self, fn):
    method _ignore_exception (line 450) | def _ignore_exception(self):
    method _serialize (line 453) | def _serialize(self, name, function):
    method _background (line 456) | def _background(self, name, function):
    method _update_event_state (line 460) | def _update_event_state(self, state, log=False):
    method _starting (line 470) | def _starting(self):
    method _fmt_msg (line 476) | def _fmt_msg(self, message):
    method _sloppy_copy (line 481) | def _sloppy_copy(self, data, name=None):
    method _create_event (line 500) | def _create_event(self):
    method _make_command_event (line 510) | def _make_command_event(self, private_data):
    method _finishing (line 517) | def _finishing(self, rv, just_cleanup=False):
    method _update_finished_event (line 538) | def _update_finished_event(self):
    method _run_sync (line 553) | def _run_sync(self, enable_cache, *args, **kwargs):
    method _run (line 598) | def _run(self, *args, **kwargs):
    method _maybe_trigger_cache_refresh (line 624) | def _maybe_trigger_cache_refresh(self):
    method record_user_activity (line 632) | def record_user_activity(self):
    method run (line 635) | def run(self, *args, **kwargs):
    method refresh (line 656) | def refresh(self):
    method command (line 660) | def command(self):
    method etag_data (line 663) | def etag_data(self):
    method max_age (line 666) | def max_age(self):
    method view (line 670) | def view(cls, result):
  function GetCommand (line 674) | def GetCommand(name):
  function Action (line 681) | def Action(session, opt, arg, data=None):

FILE: mailpile/config/base.py
  class ConfigValueError (line 15) | class ConfigValueError(ValueError):
  function ConfigRule (line 19) | def ConfigRule(*args):
  function PublicConfigRule (line 27) | def PublicConfigRule(*args):
  function KeyConfigRule (line 33) | def KeyConfigRule(*args):
  function CriticalConfigRule (line 41) | def CriticalConfigRule(*args):
  function ConfigPrinter (line 47) | def ConfigPrinter(cfg, indent=''):
  class InvalidKeyError (line 73) | class InvalidKeyError(ValueError):
  class CommentedEscapedConfigParser (line 77) | class CommentedEscapedConfigParser(ConfigParser.RawConfigParser):
    method set (line 95) | def set(self, section, key, value, comment):
    method _decode_value (line 115) | def _decode_value(self, value):
    method get (line 121) | def get(self, section, key):
    method items (line 127) | def items(self, section):
  function _MakeCheck (line 132) | def _MakeCheck(pcls, name, comment, rules, write_watcher):
  function RuledContainer (line 141) | def RuledContainer(pcls):
  class ConfigList (line 503) | class ConfigList(RuledContainer(list)):
    method reset (line 524) | def reset(self, rules=True, data=True):
    method __createkey_and_setitem__ (line 530) | def __createkey_and_setitem__(self, key, value):
    method append (line 538) | def append(self, value):
    method __passkey__ (line 547) | def __passkey__(self, key, value):
    method __fixkey__ (line 553) | def __fixkey__(self, key):
    method get (line 561) | def get(self, key, default=None):
    method __getitem__ (line 567) | def __getitem__(self, key):
    method fmt_key (line 570) | def fmt_key(self, key):
    method iterkeys (line 574) | def iterkeys(self):
    method iteritems (line 577) | def iteritems(self):
    method keys (line 581) | def keys(self):
    method all_keys (line 584) | def all_keys(self):
    method values (line 587) | def values(self):
    method update (line 590) | def update(self, *args):
  class ConfigDict (line 599) | class ConfigDict(RuledContainer(dict)):
    method reset (line 689) | def reset(self, rules=True, data=True):
    method all_keys (line 699) | def all_keys(self):
    method append (line 703) | def append(self, value):
    method update (line 712) | def update(self, *args, **kwargs):
    method parse_config (line 724) | def parse_config(self, session, data, source='internal'):
  class PathDict (line 789) | class PathDict(ConfigDict):

FILE: mailpile/config/manager.py
  class ConfigManager (line 63) | class ConfigManager(ConfigDict):
    method __init__ (line 69) | def __init__(self, workdir=None, shareddatadir=None, rules={}):
    method create_and_lock_workdir (line 149) | def create_and_lock_workdir(self, session):
    method load (line 168) | def load(self, session, *args, **kwargs):
    method load_master_key (line 196) | def load_master_key(self, passphrase, _raise=None):
    method _load_config_lines (line 236) | def _load_config_lines(self, filename, lines):
    method _discover_plugins (line 242) | def _discover_plugins(self):
    method _configure_default_plugins (line 254) | def _configure_default_plugins(self):
    method _unlocked_load (line 266) | def _unlocked_load(self, session, public_only=False):
    method reset_rules_from_source (line 370) | def reset_rules_from_source(self):
    method load_plugins (line 378) | def load_plugins(self, session):
    method save (line 392) | def save(self, *args, **kwargs):
    method get_master_key (line 396) | def get_master_key(self):
    method set_master_key (line 418) | def set_master_key(self, key):
    method _delete_old_master_keys (line 423) | def _delete_old_master_keys(self, keyfile):
    method _save_master_key (line 437) | def _save_master_key(self, keyfile):
    method _unlocked_save (line 502) | def _unlocked_save(self, session=None, force=False):
    method _find_mail_source (line 601) | def _find_mail_source(self, mbx_id, path=None):
    method get_mailboxes (line 614) | def get_mailboxes(self, with_mail_source=None,
    method is_editable_message (line 647) | def is_editable_message(self, msg_info):
    method is_editable_mailbox (line 660) | def is_editable_mailbox(self, mailbox_id):
    method load_pickle (line 671) | def load_pickle(self, pfn, delete_if_corrupt=False):
    method save_pickle (line 692) | def save_pickle(self, obj, pfn, encrypt=True):
    method _mailbox_info (line 706) | def _mailbox_info(self, mailbox_id, prefer_local=True):
    method save_mailbox (line 722) | def save_mailbox(self, session, pfn, mbox):
    method uncache_mailbox (line 727) | def uncache_mailbox(self, session, entry, drop=True, force_save=False):
    method cache_mailbox (line 770) | def cache_mailbox(self, session, pfn, mbx_id, mbox):
    method flush_mbox_cache (line 790) | def flush_mbox_cache(self, session, clear=True, wait=False):
    method find_mboxids_and_sources_by_path (line 805) | def find_mboxids_and_sources_by_path(self, *paths):
    method open_mailbox_path (line 833) | def open_mailbox_path(self, session, path, register=False, raw_open=Fa...
    method open_mailbox (line 879) | def open_mailbox(self, session, mailbox_id,
    method create_local_mailstore (line 931) | def create_local_mailstore(self, session, name=None):
    method open_local_mailbox (line 952) | def open_local_mailbox(self, session):
    method get_passphrase (line 963) | def get_passphrase(self, keyid,
    method get_profile (line 1015) | def get_profile(self, email=None):
    method get_route (line 1056) | def get_route(self, frm, rcpts=['-t']):
    method data_directory (line 1072) | def data_directory(self, ftype, mode='rb', mkdir=False):
    method data_file_and_mimetype (line 1093) | def data_file_and_mimetype(self, ftype, fpath, *args, **kwargs):
    method history_file (line 1108) | def history_file(self):
    method mailindex_file (line 1111) | def mailindex_file(self):
    method mailpile_path (line 1114) | def mailpile_path(self, path):
    method tempfile_dir (line 1126) | def tempfile_dir(self):
    method clean_tempfile_dir (line 1132) | def clean_tempfile_dir(self):
    method postinglist_dir (line 1144) | def postinglist_dir(self, prefix):
    method need_more_disk_space (line 1153) | def need_more_disk_space(self, required=0, nodefault=False, ratio=1.0):
    method interruptable_wait_for_lock (line 1164) | def interruptable_wait_for_lock(self):
    method get_index (line 1175) | def get_index(self, session):
    method get_path_index (line 1191) | def get_path_index(self, session, path):
    method get_proxy_settings (line 1209) | def get_proxy_settings(self):
    method open_file (line 1235) | def open_file(self, ftype, fpath, mode='rb', mkdir=False):
    method daemons_started (line 1244) | def daemons_started(config, which=None):
    method get_mail_source (line 1248) | def get_mail_source(config, src_id, start=False, changed=False):
    method start_tor_worker (line 1263) | def start_tor_worker(config):
    method prepare_workers (line 1272) | def prepare_workers(self, *args, **kwargs):
    method _unlocked_prepare_workers (line 1276) | def _unlocked_prepare_workers(config, session=None, changed=False,
    method _unlocked_get_all_workers (line 1444) | def _unlocked_get_all_workers(config):
    method stop_workers (line 1453) | def stop_workers(config):
    method _unlocked_notify_workers_config_changed (line 1495) | def _unlocked_notify_workers_config_changed(config):

FILE: mailpile/config/paths.py
  function _ensure_exists (line 12) | def _ensure_exists(path, mode=0o700):
  function LEGACY_DEFAULT_WORKDIR (line 20) | def LEGACY_DEFAULT_WORKDIR(profile):
  function DEFAULT_WORKDIR (line 31) | def DEFAULT_WORKDIR():
  function DEFAULT_SHARED_DATADIR (line 51) | def DEFAULT_SHARED_DATADIR():
  function DEFAULT_LOCALE_DIRECTORY (line 80) | def DEFAULT_LOCALE_DIRECTORY():
  function LOCK_PATHS (line 85) | def LOCK_PATHS(workdir=None):

FILE: mailpile/config/validators.py
  function BoolCheck (line 18) | def BoolCheck(value):
  function SlugCheck (line 48) | def SlugCheck(slug, allow=''):
  function SlashSlugCheck (line 72) | def SlashSlugCheck(slug):
  function RouteProtocolCheck (line 82) | def RouteProtocolCheck(proto):
  function DnsNameValid (line 95) | def DnsNameValid(dnsname):
  function HostNameValid (line 104) | def HostNameValid(host):
  function HostNameCheck (line 137) | def HostNameCheck(host):
  function B36Check (line 158) | def B36Check(b36val):
  function NotUnicode (line 174) | def NotUnicode(string):
  function PathCheck (line 185) | def PathCheck(path):
  function WebRootCheck (line 205) | def WebRootCheck(path):
  function FileCheck (line 226) | def FileCheck(path=None):
  function DirCheck (line 246) | def DirCheck(path=None):
  function NewPathCheck (line 266) | def NewPathCheck(path):
  function UrlCheck (line 281) | def UrlCheck(url):
  function EmailCheck (line 304) | def EmailCheck(email):
  function GPGKeyCheck (line 316) | def GPGKeyCheck(value):
  class IgnoreValue (line 359) | class IgnoreValue(Exception):
  function IgnoreCheck (line 363) | def IgnoreCheck(data):

FILE: mailpile/conn_brokers.py
  function MonkeySockCreateConn (line 82) | def MonkeySockCreateConn(*args, **kwargs):
  function _explain_encryption (line 90) | def _explain_encryption(sock):
  class Capability (line 103) | class Capability(object):
  class CapabilityFailure (line 149) | class CapabilityFailure(IOError):
  class Url (line 163) | class Url(str):
    method __init__ (line 164) | def __init__(self, *args, **kwargs):
  class BrokeredContext (line 173) | class BrokeredContext(object):
    method __init__ (line 185) | def __init__(self, broker, need=None, reject=None, oneshot=False):
    method __str__ (line 192) | def __str__(self):
    method _reset (line 215) | def _reset(self):
    method __enter__ (line 224) | def __enter__(self, *args, **kwargs):
    method __exit__ (line 239) | def __exit__(self, *args, **kwargs):
  class BaseConnectionBroker (line 243) | class BaseConnectionBroker(Capability):
    method __init__ (line 249) | def __init__(self, master=None):
    method configure (line 255) | def configure(self):
    method set_config (line 258) | def set_config(self, config):
    method config (line 262) | def config(self):
    method _raise_or_none (line 269) | def _raise_or_none(self, exc, why):
    method _check (line 274) | def _check(self, need, reject, _raise=CapabilityFailure):
    method _describe (line 289) | def _describe(self, context, conn):
    method debug (line 292) | def debug(self, val):
    method context (line 296) | def context(self, need=None, reject=None, oneshot=False):
    method create_conn_with_caps (line 299) | def create_conn_with_caps(self, address, context, need, reject,
    method create_connection (line 307) | def create_connection(self, address, *args, **kwargs):
    method _create_connection (line 319) | def _create_connection(self, context, address, *args, **kwargs):
    method get_urls (line 322) | def get_urls(self, listening_fd,
    method _get_urls (line 330) | def _get_urls(self, listening_fd,
  class TcpConnectionBroker (line 335) | class TcpConnectionBroker(BaseConnectionBroker):
    method configure (line 352) | def configure(self):
    method _describe (line 357) | def _describe(self, context, conn):
    method _in_no_proxy_list (line 370) | def _in_no_proxy_list(self, address):
    method _avoid (line 376) | def _avoid(self, address):
    method _broker_avoid (line 383) | def _broker_avoid(self, address):
    method _conn (line 387) | def _conn(self, address, *args, **kwargs):
    method _create_connection (line 392) | def _create_connection(self, context, address, *args, **kwargs):
  class SocksConnBroker (line 400) | class SocksConnBroker(TcpConnectionBroker):
    method _describe (line 418) | def _describe(self, context, conn):
    method __init__ (line 423) | def __init__(self, *args, **kwargs):
    method configure (line 428) | def configure(self):
    method _auth_args (line 444) | def _auth_args(self):
    method _avoid (line 449) | def _avoid(self, address):
    method _fix_address_tuple (line 454) | def _fix_address_tuple(self, address):
    method _conn (line 457) | def _conn(self, address, timeout=None, source_address=None, **kwargs):
  class TorConnBroker (line 482) | class TorConnBroker(SocksConnBroker):
    method _describe (line 505) | def _describe(self, context, conn):
    method _auth_args (line 510) | def _auth_args(self):
    method _fix_address_tuple (line 515) | def _fix_address_tuple(self, address):
    method _broker_avoid (line 519) | def _broker_avoid(self, address):
  class TorRiskyBroker (line 524) | class TorRiskyBroker(TorConnBroker):
  class TorOnionBroker (line 537) | class TorOnionBroker(TorConnBroker):
    method _broker_avoid (line 555) | def _broker_avoid(self, address):
  class BaseConnectionBrokerProxy (line 561) | class BaseConnectionBrokerProxy(TcpConnectionBroker):
    method _proxy_address (line 570) | def _proxy_address(self, address):
    method _proxy (line 573) | def _proxy(self, conn):
    method _wrap_ssl (line 576) | def _wrap_ssl(self, conn):
    method _create_connection (line 581) | def _create_connection(self, context, address, *args, **kwargs):
  class AutoTlsConnBroker (line 593) | class AutoTlsConnBroker(BaseConnectionBrokerProxy):
    method _describe (line 603) | def _describe(self, context, conn):
    method _proxy_address (line 607) | def _proxy_address(self, address):
    method _proxy (line 615) | def _proxy(self, conn):
  class AutoSmtpStartTLSConnBroker (line 619) | class AutoSmtpStartTLSConnBroker(BaseConnectionBrokerProxy):
  class AutoImapStartTLSConnBroker (line 623) | class AutoImapStartTLSConnBroker(BaseConnectionBrokerProxy):
  class AutoPop3StartTLSConnBroker (line 627) | class AutoPop3StartTLSConnBroker(BaseConnectionBrokerProxy):
  class MasterBroker (line 631) | class MasterBroker(BaseConnectionBroker):
    method __init__ (line 639) | def __init__(self, *args, **kwargs):
    method configure (line 646) | def configure(self):
    method _debugger (line 650) | def _debugger(self, *args, **kwargs):
    method register_broker (line 654) | def register_broker(self, priority, cb):
    method get_fd_context (line 669) | def get_fd_context(self, fileno):
    method create_conn_with_caps (line 675) | def create_conn_with_caps(self, address, context, need, reject,
    method get_urls (line 710) | def get_urls(self, listening_fd, need=None, reject=None):
  function DisableUnbrokeredConnections (line 717) | def DisableUnbrokeredConnections():
  class NetworkHistory (line 727) | class NetworkHistory(Command):
    class CommandResult (line 734) | class CommandResult(Command.CommandResult):
      method as_text (line 735) | def as_text(self):
    method command (line 743) | def command(self):
  class GetTlsCertificate (line 748) | class GetTlsCertificate(Command):
    class CommandResult (line 761) | class CommandResult(Command.CommandResult):
      method as_text (line 762) | def as_text(self):
    method command (line 769) | def command(self):
  function SslWrapOnlyOnce (line 991) | def SslWrapOnlyOnce(org_sslwrap, sock, *args, **kwargs):
  function SslContextWrapOnlyOnce (line 1009) | def SslContextWrapOnlyOnce(org_ctxwrap, self, sock, *args, **kwargs):

FILE: mailpile/crypto/aes_utils.py
  function make_cryptography_utils (line 20) | def make_cryptography_utils():
  function make_pycrypto_utils (line 54) | def make_pycrypto_utils():
  function make_dummy_utils (line 88) | def make_dummy_utils():
  function getrandbits (line 109) | def getrandbits(count):
  function aes_ctr_encrypt (line 117) | def aes_ctr_encrypt(key, iv, data):
  function aes_ctr_decrypt (line 120) | def aes_ctr_decrypt(key, iv, data):

FILE: mailpile/crypto/autocrypt.py
  function canonicalize_email (line 99) | def canonicalize_email(address):
  function parse_autocrypt_headervalue (line 122) | def parse_autocrypt_headervalue(value, optional_attrs=None):
  function extract_autocrypt_header (line 170) | def extract_autocrypt_header(msg, to=None, optional_attrs=None):
  function extract_autocrypt_gossip_headers (line 201) | def extract_autocrypt_gossip_headers(msg, to=None, optional_attrs=None):
  function make_autocrypt_header (line 212) | def make_autocrypt_header(addr, binary_key,
  function generate_autocrypt_setup_code (line 223) | def generate_autocrypt_setup_code(random_data=None):
  function UNUSED_get_minimal_PGP_key (line 249) | def UNUSED_get_minimal_PGP_key(keydata,
  class AutocryptRecommendation (line 426) | class AutocryptRecommendation(object):
    method __init__ (line 434) | def __init__(self, policy, key_sig=None):
    method __str__ (line 438) | def __str__(self):
    method Synchronize (line 444) | def Synchronize(cls, *recommendations):
    method set_recommendation (line 457) | def set_recommendation(self, policy, key_sig=None):

FILE: mailpile/crypto/gpgi.py
  class GnuPGEventUpdater (line 59) | class GnuPGEventUpdater:
    method __init__ (line 63) | def __init__(self, event):
    method _log (line 67) | def _log(self, section, message):
    method _log_private (line 72) | def _log_private(self, message):
    method _log_public (line 75) | def _log_public(self, message):
    method running_gpg (line 79) | def running_gpg(self, why):
    method update_args (line 85) | def update_args(self, args):
    method update_sent_passphrase (line 88) | def update_sent_passphrase(self):
    method _parse_gpg_line (line 91) | def _parse_gpg_line(self, line):
    method update_stdout (line 97) | def update_stdout(self, line):
    method update_stderr (line 100) | def update_stderr(self, line):
    method update_return_code (line 103) | def update_return_code(self, code):
  class GnuPGResultParser (line 107) | class GnuPGResultParser:
    method __init__ (line 111) | def __init__(rp, decrypt_requires_MDC=True, debug=None):
    method parse (line 123) | def parse(rp, retvals):
  class GnuPGRecordParser (line 247) | class GnuPGRecordParser:
    method __init__ (line 248) | def __init__(self):
    method parse (line 270) | def parse(self, lines):
    method parse_line (line 275) | def parse_line(self, line):
    method _parse_dates (line 282) | def _parse_dates(self, line):
    method _parse_keydata (line 294) | def _parse_keydata(self, line):
    method _clean_curdata (line 311) | def _clean_curdata(self):
    method parse_pubkey (line 317) | def parse_pubkey(self, line):
    method parse_subkey (line 326) | def parse_subkey(self, line):
    method parse_fingerprint (line 331) | def parse_fingerprint(self, line):
    method parse_userattribute (line 339) | def parse_userattribute(self, line):
    method parse_privkey (line 345) | def parse_privkey(self, line):
    method parse_uidline (line 348) | def parse_uidline(self, line):
    method parse_trust (line 364) | def parse_trust(self, line):
    method parse_signature (line 368) | def parse_signature(self, line):
    method parse_keygrip (line 382) | def parse_keygrip(self, line):
    method parse_revoke (line 385) | def parse_revoke(self, line):
    method parse_revocation_key (line 388) | def parse_revocation_key(self, line):
    method parse_unknown (line 391) | def parse_unknown(self, line):
    method parse_none (line 394) | def parse_none(line):
  function stubborn_decode (line 401) | def stubborn_decode(text):
  function parse_uid (line 413) | def parse_uid(uidstr):
  class StreamReader (line 429) | class StreamReader(Thread):
    method __init__ (line 430) | def __init__(self, name, fd, callback, lines=True):
    method __str__ (line 437) | def __str__(self):
    method readin (line 441) | def readin(self, fd, callback):
  class StreamWriter (line 464) | class StreamWriter(Thread):
    method __init__ (line 465) | def __init__(self, name, fd, output, partial_write_ok=False):
    method __str__ (line 472) | def __str__(self):
    method writeout (line 475) | def writeout(self, fd, output):
  class GnuPG (line 502) | class GnuPG:
    method __init__ (line 519) | def __init__(self, config,
    method prepare_passphrase (line 552) | def prepare_passphrase(self, keyid, signing=False, decrypting=False):
    method _debug_all (line 574) | def _debug_all(self, msg):
    method _debug_none (line 580) | def _debug_none(self, msg):
    method set_home (line 583) | def set_home(self, path):
    method version (line 586) | def version(self):
    method version_tuple (line 592) | def version_tuple(self, update=False):
    method gnupghome (line 601) | def gnupghome(self):
    method is_available (line 610) | def is_available(self):
    method common_args (line 620) | def common_args(self,
    method run (line 673) | def run(self, *args, **kwargs):
    method run_without_status (line 685) | def run_without_status(self,
    method _reap_threads (line 778) | def _reap_threads(self):
    method parse_status (line 786) | def parse_status(self, line, *args):
    method parse_stdout (line 794) | def parse_stdout(self, line):
    method parse_stderr (line 799) | def parse_stderr(self, line):
    method parse_keylist (line 804) | def parse_keylist(self, keylist):
    method list_keys (line 808) | def list_keys(self, selectors=None):
    method list_secret_keys (line 824) | def list_secret_keys(self, selectors=None):
    method import_keys (line 871) | def import_keys(self,
    method _parse_import (line 892) | def _parse_import(self, output):
    method decrypt (line 948) | def decrypt(self, data,
    method base64_segment (line 1001) | def base64_segment(self, dec_start, dec_end, skip, line_len, line_end ...
    method pgp_packet_hdr_parse (line 1018) | def pgp_packet_hdr_parse(self, header, prev_partial = False):
    method sniff (line 1089) | def sniff(self, data, encoding = None):
    method remove_armor (line 1223) | def remove_armor(self, text):
    method verify (line 1234) | def verify(self, data, signature=None):
    method encrypt (line 1256) | def encrypt(self, data, tokeys=[], armor=True,
    method sign (line 1294) | def sign(self, data,
    method sign_key (line 1326) | def sign_key(self, keyid, signingkey=None):
    method delete_key (line 1338) | def delete_key(self, key_fingerprint):
    method recv_key (line 1342) | def recv_key(self, keyid,
    method parse_hpk_response (line 1359) | def parse_hpk_response(self, lines):
    method search_key (line 1389) | def search_key(self, term,
    method get_pubkey (line 1405) | def get_pubkey(self, keyid):
    method get_minimal_key (line 1408) | def get_minimal_key(self, key_id=None, user_id=None, armor=True):
    method export_pubkeys (line 1424) | def export_pubkeys(self, selectors=None, armor=True, extra_args=[]):
    method export_privkeys (line 1433) | def export_privkeys(self, selectors=None):
    method address_to_keys (line 1439) | def address_to_keys(self, address):
    method _escape_hex_keyid_term (line 1448) | def _escape_hex_keyid_term(self, term):
    method chat (line 1464) | def chat(self, gpg_args, callback, *args, **kwargs):
  function GetKeys (line 1494) | def GetKeys(gnupg, config, people):
  class OpenPGPMimeSigningWrapper (line 1559) | class OpenPGPMimeSigningWrapper(MimeSigningWrapper):
    method crypto (line 1565) | def crypto(self):
    method get_keys (line 1568) | def get_keys(self, who):
  class OpenPGPMimeEncryptingWrapper (line 1572) | class OpenPGPMimeEncryptingWrapper(MimeEncryptingWrapper):
    method crypto (line 1579) | def crypto(self):
    method get_keys (line 1582) | def get_keys(self, who):
  class OpenPGPMimeSignEncryptWrapper (line 1586) | class OpenPGPMimeSignEncryptWrapper(OpenPGPMimeEncryptingWrapper):
    method crypto (line 1591) | def crypto(self):
    method _encrypt (line 1594) | def _encrypt(self, message_text, tokeys=None, armor=False):
    method _update_crypto_status (line 1601) | def _update_crypto_status(self, part):
  class GnuPGExpectScript (line 1606) | class GnuPGExpectScript(threading.Thread):
    method __init__ (line 1619) | def __init__(self, gnupg,
    method __str__ (line 1636) | def __str__(self):
    method in_state (line 1642) | def in_state(self, state):
    method set_state (line 1645) | def set_state(self, state):
    method sendline (line 1649) | def sendline(self, proc, line):
    method _expecter (line 1668) | def _expecter(self, proc, exp, timebox):
    method expect_exact (line 1686) | def expect_exact(self, proc, exp, timeout=None):
    method run_script (line 1705) | def run_script(self, proc, script):
    method gpg_args (line 1716) | def gpg_args(self):
    method run (line 1719) | def run(self):
    method on_complete (line 1737) | def on_complete(self, name, callback):
  class GnuPGBaseKeyGenerator (line 1746) | class GnuPGBaseKeyGenerator(GnuPGExpectScript):
    method __init__ (line 1769) | def __init__(self, *args, **kwargs):
    method in_state (line 1773) | def in_state(self, state):
    method run (line 1777) | def run(self):
  class GnuPG14KeyGenerator (line 1790) | class GnuPG14KeyGenerator(GnuPGBaseKeyGenerator):
    method gpg_args (line 1811) | def gpg_args(self):
  class GnuPG21RSAKeyGenerator (line 1815) | class GnuPG21RSAKeyGenerator(GnuPG14KeyGenerator):
    method sendline (line 1834) | def sendline(self, proc, line):
    method gpg_args (line 1839) | def gpg_args(self):
  class GnuPG21Curve25519KeyGenerator (line 1845) | class GnuPG21Curve25519KeyGenerator(GnuPG21RSAKeyGenerator):
  class GnuPGDummyKeyGenerator (line 1865) | class GnuPGDummyKeyGenerator(GnuPGBaseKeyGenerator):
    method __init__ (line 1870) | def __init__(self, *args, **kwargs):
    method run (line 1874) | def run(self):
  function GnuPGKeyGenerator (line 1883) | def GnuPGKeyGenerator(gnupg, **kwargs):

FILE: mailpile/crypto/keyinfo.py
  function monkey_patch_pgpdump (line 13) | def monkey_patch_pgpdump():
  class ustr (line 38) | class ustr(str):
    method __new__ (line 39) | def __new__(cls, content):
  class RestrictedDict (line 43) | class RestrictedDict(dict):
    method prep_properties (line 47) | def prep_properties(cls):
    method __init__ (line 53) | def __init__(self, *args, **kwargs):
    method keys (line 62) | def keys(self):
    method __setitem__ (line 68) | def __setitem__(self, item, value):
    method __getitem__ (line 89) | def __getitem__(self, item):
  class KeyUID (line 96) | class KeyUID(RestrictedDict):
    method __repr__ (line 102) | def __repr__(self):
  class KeyInfo (line 113) | class KeyInfo(RestrictedDict):
    method summary (line 144) | def summary(self, full_fingerprint=False):
    method __repr__ (line 162) | def __repr__(self):
    method ensure_autocrypt_uid (line 169) | def ensure_autocrypt_uid(keyinfo, ac_uid):
    method add_subkey_capabilities (line 181) | def add_subkey_capabilities(keyinfo, now=None):
    method synthesize_validity (line 194) | def synthesize_validity(keyinfo, now=None):
    method recalculate_expiration (line 202) | def recalculate_expiration(keyinfo, now=None):
    method FromGPGI (line 221) | def FromGPGI(cls, gpgi_keyinfo):
  class MailpileKeyInfo (line 241) | class MailpileKeyInfo(KeyInfo):
  function get_keyinfo (line 260) | def get_keyinfo(data, autocrypt_header=None,

FILE: mailpile/crypto/mime.py
  function Normalize (line 22) | def Normalize(payload):
  function MessageAsString (line 39) | def MessageAsString(part, unixfrom=False):
  class EncryptionFailureError (line 45) | class EncryptionFailureError(ValueError):
    method __init__ (line 46) | def __init__(self, message, to_keys):
  class SignatureFailureError (line 51) | class SignatureFailureError(ValueError):
    method __init__ (line 52) | def __init__(self, message, from_key):
  function _decode_text_part (line 60) | def _decode_text_part(part, payload, charsets=None):
  function _update_text_payload (line 70) | def _update_text_payload(part, payload, charsets=None):
  function MimeAttachmentDisposition (line 81) | def MimeAttachmentDisposition(part, kind, newpart):
  function MimeReplacePart (line 109) | def MimeReplacePart(part, newpart, keep_old_headers=False):
  function UnwrapMimeCrypto (line 152) | def UnwrapMimeCrypto(part, protocols=None, psi=None, pei=None, charsets=...
  function UnwrapPlainTextCrypto (line 387) | def UnwrapPlainTextCrypto(part, protocols=None, psi=None, pei=None,
  function ObscureSubject (line 434) | def ObscureSubject(subject):
  function ObscureNames (line 441) | def ObscureNames(hdr):
  function ObscureSender (line 453) | def ObscureSender(sender):
  function ObscureAllRecipients (line 460) | def ObscureAllRecipients(sender):
  class MimeWrapper (line 509) | class MimeWrapper:
    method __init__ (line 527) | def __init__(self, config,
    method crypto (line 558) | def crypto(self):
    method attach (line 561) | def attach(self, part):
    method get_keys (line 579) | def get_keys(self, people):
    method flatten (line 582) | def flatten(self, msg, unixfrom=False):
    method get_only_text_part (line 585) | def get_only_text_part(self, msg):
    method wrap (line 599) | def wrap(self, msg, **kwargs):
    method prepare_wrap (line 603) | def prepare_wrap(self, msg):
    method force_display_headers (line 637) | def force_display_headers(self, msg, obscured_set):
  class MimeSigningWrapper (line 680) | class MimeSigningWrapper(MimeWrapper):
    method __init__ (line 686) | def __init__(self, *args, **kwargs):
    method _wrap_sig_in_html (line 699) | def _wrap_sig_in_html(self, sig):
    method _wrap_sig_in_html_vars (line 706) | def _wrap_sig_in_html_vars(self, sig):
    method _update_crypto_status (line 721) | def _update_crypto_status(self, part):
    method wrap (line 724) | def wrap(self, msg, prefer_inline=False):
  class MimeEncryptingWrapper (line 761) | class MimeEncryptingWrapper(MimeWrapper):
    method __init__ (line 767) | def __init__(self, *args, **kwargs):
    method _encrypt (line 783) | def _encrypt(self, message_text, tokeys=None, armor=False):
    method _update_crypto_status (line 787) | def _update_crypto_status(self, part):
    method _add_padding (line 790) | def _add_padding(self, text, chunksize=None):
    method wrap (line 799) | def wrap(self, msg, prefer_inline=False):

FILE: mailpile/crypto/records.py
  class _SimpleList (line 128) | class _SimpleList(object):
    method append (line 130) | def append(self, value):
    method extend (line 136) | def extend(self, values):
    method __getslice__ (line 140) | def __getslice__(self, i, j):
    method __iadd__ (line 143) | def __iadd__(self, y):
    method __iter__ (line 146) | def __iter__(self):
    method __reversed__ (line 149) | def __reversed__(self):
  class EncryptedRecordStore (line 153) | class EncryptedRecordStore(_SimpleList):
    method __init__ (line 170) | def __init__(self, fn, key,
    method _calculate_constants (line 199) | def _calculate_constants(self):
    method _parse_header (line 212) | def _parse_header(self):
    method _write_header (line 245) | def _write_header(self):
    method close (line 254) | def close(self):
    method _iv (line 258) | def _iv(self, pos):
    method save_record (line 276) | def save_record(self, pos, data):
    method save_zeros (line 310) | def save_zeros(self, pos, count):
    method load_record (line 318) | def load_record(self, pos):
    method __getitem__ (line 353) | def __getitem__(self, pos):
    method __setitem__ (line 359) | def __setitem__(self, pos, data):
    method get (line 362) | def get(self, pos, default=None):
    method __len__ (line 368) | def __len__(self):
  class EncryptedBlobStore (line 375) | class EncryptedBlobStore(_SimpleList):
    method __init__ (line 386) | def __init__(self, base_fn, key,
    method _big (line 408) | def _big(self):
    method _next_shardname (line 420) | def _next_shardname(self):
    method _load_next_shard (line 423) | def _load_next_shard(self):
    method __getitem__ (line 431) | def __getitem__(self, pos):
    method __setitem__ (line 440) | def __setitem__(self, pos, data):
    method __len__ (line 453) | def __len__(self):
    method close (line 458) | def close(self):
  class EncryptedUnicodeStore (line 468) | class EncryptedUnicodeStore(EncryptedBlobStore):
    method __setitem__ (line 472) | def __setitem__(self, pos, data):
    method __getitem__ (line 475) | def __getitem__(self, pos):
  class EncryptedDict (line 479) | class EncryptedDict(object):
    method __init__ (line 502) | def __init__(self, base_fn, key,
    method reset_counters (line 547) | def reset_counters(self):
    method _keyfile_size (line 553) | def _keyfile_size(self, kfi):
    method _keyfile_bytes (line 556) | def _keyfile_bytes(self, kfi):
    method _next_keyfile (line 559) | def _next_keyfile(self):
    method _load_next_keys (line 563) | def _load_next_keys(self):
    method _digest (line 585) | def _digest(self, key):
    method _offset (line 594) | def _offset(self, digest):
    method _offset_and_digest (line 597) | def _offset_and_digest(self, key):
    method load_records (line 601) | def load_records(self, kfi, keyset, count, pos, want=[]):
    method load_record (line 617) | def load_record(self, key, **kwargs):
    method load_digest_record (line 620) | def load_digest_record(self, digest, want=None):
    method _try_save (line 635) | def _try_save(self, kfi, keyset, pos, digest, value, on_fail=None):
    method save_digest_record (line 675) | def save_digest_record(self, digest, value, on_fail=None):
    method save_record (line 695) | def save_record(self, key, value, on_fail=None):
    method __setitem__ (line 699) | def __setitem__(self, key, value):
    method __contains__ (line 702) | def __contains__(self, key):
    method __delitem__ (line 709) | def __delitem__(self, key):
    method values (line 716) | def values(self):
    method rdata_digest (line 724) | def rdata_digest(self, rdata):
    method rdata_value (line 727) | def rdata_value(self, rdata):
    method __getitem__ (line 736) | def __getitem__(self, key):
    method get (line 743) | def get(self, key, default=None, **kwargs):
    method close (line 749) | def close(self):
  class EncryptedUnicodeDict (line 756) | class EncryptedUnicodeDict(EncryptedDict):
    method save_record (line 760) | def save_record(self, key, value):
    method rdata_value (line 764) | def rdata_value(self, rdata):
  class EncryptedIntDict (line 769) | class EncryptedIntDict(EncryptedDict):
    method keys (line 777) | def keys(self):
    method save_record (line 786) | def save_record(self, key, value):
    method rdata_value (line 791) | def rdata_value(self, rdata):

FILE: mailpile/crypto/state.py
  class KeyLookupError (line 9) | class KeyLookupError(ValueError):
    method __init__ (line 10) | def __init__(self, message, missing):
  class CryptoInfo (line 24) | class CryptoInfo(dict):
    method __init__ (line 30) | def __init__(self, parent=None, copy=None, bubbly=True):
    method _set_status (line 49) | def _set_status(self, value):
    method __setitem__ (line 56) | def __setitem__(self, item, value):
    method _overwrite_with (line 67) | def _overwrite_with(self, ci):
    method bubble_up (line 72) | def bubble_up(self, parent=None):
    method mix_bubbles (line 88) | def mix_bubbles(self):
    method _mix_in (line 96) | def _mix_in(self, ci):
  class EncryptionInfo (line 120) | class EncryptionInfo(CryptoInfo):
  class SignatureInfo (line 129) | class SignatureInfo(CryptoInfo):

FILE: mailpile/crypto/streamer.py
  function mac_sha256 (line 88) | def mac_sha256(key, data):
  class IOFilter (line 94) | class IOFilter(threading.Thread):
    method __init__ (line 101) | def __init__(self, fd, callback,
    method __str__ (line 126) | def __str__(self):
    method __enter__ (line 129) | def __enter__(self):
    method __exit__ (line 132) | def __exit__(self, exc_type, exc_val, exc_tb):
    method close (line 135) | def close(self):
    method join (line 148) | def join(self, aborting=None):
    method writer (line 153) | def writer(self):
    method reader (line 165) | def reader(self):
    method _copy_loop (line 178) | def _copy_loop(self):
    method run (line 191) | def run(self):
  class ReadLineIOFilter (line 221) | class ReadLineIOFilter(IOFilter):
    method __init__ (line 226) | def __init__(self, fd, callback,
    method _maybe_flush (line 233) | def _maybe_flush(self, eof=False):
    method _copy_loop (line 247) | def _copy_loop(self):
  class IOCoprocess (line 265) | class IOCoprocess(object):
    method __init__ (line 266) | def __init__(self, command, fd, name=None, long_running=False):
    method __enter__ (line 283) | def __enter__(self):
    method __exit__ (line 286) | def __exit__(self, exc_type, exc_val, exc_tb):
    method close (line 289) | def close(self, *args):
  class OutputCoprocess (line 318) | class OutputCoprocess(IOCoprocess):
    method _popen (line 322) | def _popen(self, command, fd, long_running):
    method _write_filter (line 327) | def _write_filter(self, data):
    method write (line 330) | def write(self, data, *args, **kwargs):
    method flush (line 333) | def flush(self):
  class InputCoprocess (line 337) | class InputCoprocess(IOCoprocess):
    method _popen (line 341) | def _popen(self, command, fd, long_running):
    method _read_filter (line 347) | def _read_filter(self, data):
    method __iter__ (line 350) | def __iter__(self, *args):
    method readline (line 353) | def readline(self, *args):
    method readlines (line 356) | def readlines(self, *args):
    method read (line 359) | def read(self, *args):
  class ChecksummingStreamer (line 363) | class ChecksummingStreamer(OutputCoprocess):
    method __init__ (line 370) | def __init__(self, dir=None, name=None, long_running=False,
    method _mk_tempfile_and_path (line 404) | def _mk_tempfile_and_path(self, _dir):
    method _mk_command (line 408) | def _mk_command(self):
    method finish (line 411) | def finish(self):
    method close (line 433) | def close(self):
    method save (line 437) | def save(self, filename, finish=True, mode='wb'):
    method calculate_outer_sha256 (line 469) | def calculate_outer_sha256(self):
    method outer_mac_sha256 (line 480) | def outer_mac_sha256(self):
    method save_copy (line 484) | def save_copy(self, ofd):
    method _sha256_callback (line 491) | def _sha256_callback(self, data):
    method _write_preamble (line 502) | def _write_preamble(self):
    method _write_postamble (line 505) | def _write_postamble(self):
  class EncryptingDelimitedStreamer (line 509) | class EncryptingDelimitedStreamer(ChecksummingStreamer):
    method __init__ (line 523) | def __init__(self, key,
    method _write_filter (line 570) | def _write_filter(self, data):
    method _sha256_callback (line 588) | def _sha256_callback(self, data):
    method outer_mac_sha256 (line 591) | def outer_mac_sha256(self):
    method write_pad_and_flush (line 594) | def write_pad_and_flush(self, data, pad=' '):
    method finish (line 604) | def finish(self, *args, **kwargs):
    method _write_inner_sha256 (line 614) | def _write_inner_sha256(self):
    method _nonce_and_mutated_key (line 629) | def _nonce_and_mutated_key(self, key):
    method _send_key (line 638) | def _send_key(self):
    method _mk_command (line 644) | def _mk_command(self):
    method _write_preamble (line 650) | def _write_preamble(self):
    method _write_postamble (line 661) | def _write_postamble(self):
  class EncryptingUndelimitedStreamer (line 668) | class EncryptingUndelimitedStreamer(EncryptingDelimitedStreamer):
  function EncryptingStreamer (line 681) | def EncryptingStreamer(*args, **kwargs):
  class DecryptingStreamer (line 691) | class DecryptingStreamer(InputCoprocess):
    method StartEncrypted (line 712) | def StartEncrypted(cls, line):
    method EndEncrypted (line 718) | def EndEncrypted(cls, line):
    method __init__ (line 722) | def __init__(self, fd,
    method _read_filter (line 768) | def _read_filter(self, data):
    method close (line 776) | def close(self):
    method verify (line 781) | def verify(self, testing=False, _raise=None):
    method _mk_data_filter (line 820) | def _mk_data_filter(self, fd, cb, ecb):
    method _read_data (line 824) | def _read_data(self, data):
    method _mutate_key (line 985) | def _mutate_key(self, key, nonce):
    method _mk_command (line 988) | def _mk_command(self):
  class PartialDecryptingStreamer (line 1003) | class PartialDecryptingStreamer(DecryptingStreamer):
    method __init__ (line 1004) | def __init__(self, start_data, *args, **kwargs):
    method _mk_data_filter (line 1008) | def _mk_data_filter(self, fd, cb, ecb):
  function _assert (line 1020) | def _assert(val, want=True, msg='assert'):
  function fdcheck (line 1060) | def fdcheck(where):
  function counter (line 1076) | def counter(data):

FILE: mailpile/crypto/tor.py
  function debug (line 25) | def debug( text ):
  class Tor (line 34) | class Tor(threading.Thread):
    method __new__ (line 36) | def __new__(cls, *args, **kwargs):
    method __init__ (line 41) | def __init__(self, session=None, config=None,
    method run (line 73) | def run(self):
    method _run_once (line 85) | def _run_once(self):
    method _hashed_control_password (line 150) | def _hashed_control_password(self):
    method _log_line (line 164) | def _log_line(self, line, notify=False):
    method relaunch_hidden_services (line 183) | def relaunch_hidden_services(self):
    method launch_hidden_service (line 191) | def launch_hidden_service(self, portmap, key_type=None, key_content=No...
    method stop_tor (line 207) | def stop_tor(self, wait=True):
    method isReady (line 225) | def isReady(self, wait=False):
    method isAlive (line 239) | def isAlive(self):
    method quit (line 243) | def quit(self, join=True):

FILE: mailpile/eventlog.py
  function NewEventId (line 22) | def NewEventId():
  function _ClassName (line 34) | def _ClassName(obj, ignore_regexps=False):
  class Event (line 48) | class Event(object):
    method Parse (line 66) | def Parse(cls, json_string):
    method __init__ (line 72) | def __init__(self,
    method __str__ (line 86) | def __str__(self):
    method _set_ts (line 89) | def _set_ts(self, ts):
    method _set (line 98) | def _set(self, col, value):
    method _get_source_class (line 102) | def _get_source_class(self):
    method as_dict (line 125) | def as_dict(self, private=True):
    method as_text (line 142) | def as_text(self, private=True, compact=False):
    method as_json (line 155) | def as_json(self, private=True):
    method as_html (line 161) | def as_html(self, private=True):
  function GetThreadEvent (line 171) | def GetThreadEvent(create=False, message=None, source=None):
  class EventLog (line 181) | class EventLog(object):
    method __init__ (line 193) | def __init__(self, logdir, decryption_key_func, encryption_key_func,
    method _notify_waiters (line 208) | def _notify_waiters(self):
    method wait (line 212) | def wait(self, timeout=None):
    method _save_filename (line 216) | def _save_filename(self):
    method _open_log (line 219) | def _open_log(self):
    method _maybe_rotate_log (line 247) | def _maybe_rotate_log(self):
    method _list_logfiles (line 257) | def _list_logfiles(self):
    method _save_events (line 261) | def _save_events(self, events, recursed=False):
    method _load_logfile (line 276) | def _load_logfile(self, lfn):
    method _match (line 292) | def _match(self, event, filters):
    method incomplete (line 334) | def incomplete(self, **filters):
    method since (line 347) | def since(self, ts, **filters):
    method events (line 362) | def events(self, **filters):
    method get (line 365) | def get(self, event_id, default=None):
    method log_event (line 368) | def log_event(self, event):
    method log (line 379) | def log(self, *args, **kwargs):
    method close (line 383) | def close(self):
    method _unlocked_close (line 387) | def _unlocked_close(self):
    method _prune_completed (line 394) | def _prune_completed(self):
    method ui_watch (line 399) | def ui_watch(self, ui):
    method ui_unwatch (line 408) | def ui_unwatch(self, ui):
    method load (line 416) | def load(self):
    method purge_old_logfiles (line 429) | def purge_old_logfiles(self, keep=None):

FILE: mailpile/httpd.py
  function Idle_HTTPD (line 38) | def Idle_HTTPD(allowed=1):
  class HttpRequestHandler (line 49) | class HttpRequestHandler(SimpleXMLRPCRequestHandler):
    method assert_no_newline (line 86) | def assert_no_newline(self, data):
    method assert_no_html (line 90) | def assert_no_html(self, data):
    method send_header (line 94) | def send_header(self, hdr, value):
    method http_host (line 98) | def http_host(self):
    method _load_cookies (line 106) | def _load_cookies(self):
    method http_session (line 117) | def http_session(self):
    method server_url (line 127) | def server_url(self):
    method send_http_response (line 137) | def send_http_response(self, code, msg):
    method send_http_redirect (line 143) | def send_http_redirect(self, destination):
    method send_standard_headers (line 157) | def send_standard_headers(self,
    method send_full_response (line 198) | def send_full_response(self, message,
    method guess_mimetype (line 233) | def guess_mimetype(self, fpath):
    method _mk_etag (line 239) | def _mk_etag(self, *args):
    method _maybe_gzip (line 246) | def _maybe_gzip(self, data, msg_size, headers):
    method send_file (line 265) | def send_file(self, config, filename, suppress_body=False):
    method do_POST (line 304) | def do_POST(self, method='POST'):
    method do_GET (line 343) | def do_GET(self, *args, **kwargs):
    method _real_do_GET (line 360) | def _real_do_GET(self, post_data={}, suppress_body=False, method='GET'):
    method do_PUT (line 525) | def do_PUT(self):
    method do_UPDATE (line 528) | def do_UPDATE(self):
    method do_HEAD (line 531) | def do_HEAD(self):
    method log_message (line 534) | def log_message(self, fmt, *args):
  class HttpServer (line 540) | class HttpServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
    method __init__ (line 541) | def __init__(self, session, sspec, handler):
    method serve_forever (line 572) | def serve_forever(self, poll_interval=0.5, tick_func=None):
    method shutdown (line 594) | def shutdown(self, join=True):
    method make_session_id (line 600) | def make_session_id(self, request):
    method finish_request (line 608) | def finish_request(self, request, client_address):
  class HttpWorker (line 620) | class HttpWorker(threading.Thread):
    method __init__ (line 621) | def __init__(self, session, sspec):
    method idle_tick (line 628) | def idle_tick(self, httpd):
    method run (line 631) | def run(self):
    method quit (line 645) | def quit(self, join=False):

FILE: mailpile/i18n.py
  function _fmt_safe (line 19) | def _fmt_safe(translation, original):
  function gettext (line 40) | def gettext(string):
  function ngettext (line 61) | def ngettext(string1, string2, n):
  class i18n_disabler (line 84) | class i18n_disabler:
    method __init__ (line 85) | def __init__(self):
    method __enter__ (line 88) | def __enter__(self):
    method __exit__ (line 93) | def __exit__(self, *args, **kwargs):
  function ActivateTranslation (line 101) | def ActivateTranslation(session, config, language, localedir=None):
  function ListTranslations (line 151) | def ListTranslations(config, localedir=None):

FILE: mailpile/index/base.py
  class BaseIndex (line 19) | class BaseIndex(MessageInfoConstants):
    method __init__ (line 32) | def __init__(self, config):
    method add_email (line 42) | def add_email(self, email, name=None, eid=None):
    method update_email (line 51) | def update_email(self, email, name=None, change_name=True):
    method compact_to_list (line 61) | def compact_to_list(self, msg_to):
    method expand_to_list (line 73) | def expand_to_list(self, msg_info, field=None):
    method remove_tag (line 81) | def remove_tag(self, session, tid, msg_idxs=None):
    method add_tag (line 84) | def add_tag(self, session, tid, msg_idxs=None):
    method apply_filters (line 87) | def apply_filters(self, session, fid, msg_idxs=None):
    method search (line 93) | def search(self, session, terms, context=None):
    method sort_results (line 96) | def sort_results(self, session, results, sort_order):
    method get_conversation (line 99) | def get_conversation(self, msg_idx=None):
    method get_msg_at_idx_pos_uncached (line 105) | def get_msg_at_idx_pos_uncached(self, msg_idx):
    method open_mailbox_by_ptr (line 108) | def open_mailbox_by_ptr(self, msg_ptr):
    method unique_mbox_ids (line 114) | def unique_mbox_ids(self, msg_info):
    method enumerate_ptrs_mboxes_fds (line 118) | def enumerate_ptrs_mboxes_fds(self, msg_info):
    method _sorted_msg_ptrs (line 132) | def _sorted_msg_ptrs(self, msg_info):
    method _encode_msg_id (line 139) | def _encode_msg_id(self, msg_id):
    method get_msg_id (line 147) | def get_msg_id(self, msg, msg_ptr):
    method _message_to_msg_info (line 150) | def _message_to_msg_info(self, msg_idx_pos, msg_ptr, msg):
    method get_msg_at_idx_pos (line 170) | def get_msg_at_idx_pos(self, msg_idx):
    method set_msg_at_idx_pos (line 191) | def set_msg_at_idx_pos(self, msg_idx, msg_info):
    method get_body (line 197) | def get_body(self, msg_info):
    method truncate_body_snippet (line 215) | def truncate_body_snippet(self, body, max_chars):
    method encode_body (line 222) | def encode_body(self, d, **kwargs):
    method set_body (line 236) | def set_body(self, msg_info, **kwargs):

FILE: mailpile/index/mailboxes.py
  class MailboxIndex (line 15) | class MailboxIndex(BaseIndex):
    method __init__ (line 19) | def __init__(self, config, mailbox, mbx_mid=None):
    method get_msg_at_idx_pos_uncached (line 26) | def get_msg_at_idx_pos_uncached(self, msg_idx_pos):
    method open_mailbox_by_ptr (line 32) | def open_mailbox_by_ptr(self, msg_ptr):
    method _update_keymap (line 38) | def _update_keymap(self):
    method search (line 50) | def search(self, session, terms, context=None):

FILE: mailpile/index/msginfo.py
  class MessageInfoConstants (line 6) | class MessageInfoConstants(object):

FILE: mailpile/index/search.py
  class SearchResultSet (line 8) | class SearchResultSet:
    method __init__ (line 12) | def __init__(self, idx, terms, results, exclude):
    method set_results (line 17) | def set_results(self, results, exclude):
    method __len__ (line 24) | def __len__(self):
    method as_set (line 27) | def as_set(self, order='raw'):
    method excluded (line 30) | def excluded(self):
  class CachedSearchResultSet (line 37) | class CachedSearchResultSet(SearchResultSet):
    method __init__ (line 41) | def __init__(self, idx, terms):
    method _skey (line 48) | def _skey(self):
    method set_results (line 51) | def set_results(self, *args):
    method DropCaches (line 59) | def DropCaches(cls, msg_idxs=None, tags=None):

FILE: mailpile/mail_source/__init__.py
  class BaseMailSource (line 24) | class BaseMailSource(threading.Thread):
    class MailSourceVfs (line 39) | class MailSourceVfs(MailpileVfsBase):
      method __init__ (line 41) | def __init__(self, config, source, *args, **kwargs):
      method _get_mbox_id (line 47) | def _get_mbox_id(self, path):
      method Handles (line 50) | def Handles(self, path):
      method glob_ (line 55) | def glob_(self, *args, **kwargs):
      method listdir_ (line 58) | def listdir_(self, where, **kwargs):
      method open_ (line 61) | def open_(self, fp, *args, **kwargs):
      method abspath_ (line 64) | def abspath_(self, path):
      method isdir_ (line 78) | def isdir_(self, fp):
      method ismailsource_ (line 81) | def ismailsource_(self, fp):
      method mailbox_type_ (line 84) | def mailbox_type_(self, fp, config):
      method getsize_ (line 87) | def getsize_(self, path):
      method display_name_ (line 90) | def display_name_(self, path, config):
      method exists_ (line 100) | def exists_(self, fp):
    method __init__ (line 105) | def __init__(self, session, my_config):
    method __str__ (line 129) | def __str__(self):
    method _pfn (line 135) | def _pfn(self):
    method _load_state (line 138) | def _load_state(self):
    method _save_state (line 164) | def _save_state(self):
    method _save_config (line 167) | def _save_config(self):
    method _log_status (line 171) | def _log_status(self, message, clear_errors=False):
    method open (line 184) | def open(self):
    method close (line 188) | def close(self):
    method _has_mailbox_changed (line 192) | def _has_mailbox_changed(self, mbx, state):
    method _mark_mailbox_rescanned (line 197) | def _mark_mailbox_rescanned(self, mbx, state):
    method _path (line 202) | def _path(self, mbx):
    method _check_interrupt (line 208) | def _check_interrupt(self, log=True, clear=True):
    method _mailbox_sort_key (line 227) | def _mailbox_sort_key(self, m):
    method _sorted_mailboxes (line 230) | def _sorted_mailboxes(self):
    method _policy (line 240) | def _policy(self, mbx_cfg):
    method update_name_to_match_tag (line 246) | def update_name_to_match_tag(self):
    method sync_mail (line 255) | def sync_mail(self):
    method _jitter (line 395) | def _jitter(self, seconds):
    method _sleeping_is_ok (line 398) | def _sleeping_is_ok(self, slept):
    method _sleep (line 401) | def _sleep(self, seconds):
    method _existing_mailboxes (line 421) | def _existing_mailboxes(self):
    method _update_unknown_state (line 427) | def _update_unknown_state(self):
    method reset_event_discovery_state (line 435) | def reset_event_discovery_state(self):
    method set_event_discovery_state (line 440) | def set_event_discovery_state(self, state=None, error=None, status=None):
    method on_event_discovery_starting (line 450) | def on_event_discovery_starting(self):
    method on_event_discovery_toomany (line 457) | def on_event_discovery_toomany(self):
    method on_event_discovery_done (line 470) | def on_event_discovery_done(self, ostate):
    method discover_mailboxes (line 474) | def discover_mailboxes(self, paths=None):
    method _default_policy (line 564) | def _default_policy(self, mbx_cfg):
    method take_over_mailbox (line 567) | def take_over_mailbox(self, mailbox_idx,
    method _guess_tags (line 596) | def _guess_tags(self, mbx_cfg):
    method _strip_file_extension (line 603) | def _strip_file_extension(self, path):
    method _mailbox_path_split (line 606) | def _mailbox_path_split(self, path):
    method _mailbox_name (line 609) | def _mailbox_name(self, path):
    method _create_local_mailbox (line 612) | def _create_local_mailbox(self, mbx_cfg, save=True):
    method _create_parent_tag (line 636) | def _create_parent_tag(self, save=True):
    method _create_primary_tag (line 663) | def _create_primary_tag(self, mbx_cfg, save=True):
    method _path_to_tagname (line 702) | def _path_to_tagname(self, path):  # -> tag name
    method _unique_tag_name (line 713) | def _unique_tag_name(self, tagname):  # -> unused tag name
    method _create_tag_name (line 723) | def _create_tag_name(self, path):  # -> unique tag name
    method _create_tag (line 727) | def _create_tag(self, tag_name_or_id,
    method interrupt_rescan (line 769) | def interrupt_rescan(self, reason):
    method _process_new (line 774) | def _process_new(self, mbx_key, mbx_cfg, mbox,
    method _msg_key_order (line 782) | def _msg_key_order(self, key):
    method _copy_new_messages (line 785) | def _copy_new_messages(self, mbx_key, mbx_cfg, src,
    method rescan_mailbox (line 932) | def rescan_mailbox(self, mbx_key, mbx_cfg, path, stop_after=None):
    method open_mailbox (line 1018) | def open_mailbox(self, mbx_id, fn):
    method is_mailbox (line 1023) | def is_mailbox(self, fn):
    method _summarize_auth (line 1026) | def _summarize_auth(self):
    method run (line 1031) | def run(self):
    method _check_keepalive (line 1121) | def _check_keepalive(self):
    method _log_conn_errors (line 1125) | def _log_conn_errors(self):
    method wake_up (line 1133) | def wake_up(self):
    method notify_config_changed (line 1138) | def notify_config_changed(self):
    method rescan_now (line 1143) | def rescan_now(self, session=None, started_callback=None):
    method quit (line 1176) | def quit(self, join=False):
  function ProcessNew (line 1184) | def ProcessNew(session, msg, msg_metadata_kws, msg_ts, keywords, snippet):
  function MailSource (line 1201) | def MailSource(session, my_config):

FILE: mailpile/mail_source/imap.py
  class IMAP_IOError (line 97) | class IMAP_IOError(IOError):
  class WithaBool (line 101) | class WithaBool(object):
    method __init__ (line 102) | def __init__(self, v): self.v = v
    method __nonzero__ (line 103) | def __nonzero__(self): return self.v
    method __enter__ (line 104) | def __enter__(self, *a, **kw): return self.v
    method __exit__ (line 105) | def __exit__(self, *a, **kw): return self.v
  function _parse_imap (line 108) | def _parse_imap(reply):
  class ImapMailboxIndex (line 148) | class ImapMailboxIndex(MailboxIndex):
  class SharedImapConn (line 152) | class SharedImapConn(threading.Thread):
    method __init__ (line 162) | def __init__(self, session, conn, idle_mailbox=None, idle_callback=None):
    method _mk_proxy (line 182) | def _mk_proxy(self, method):
    method _shutdown (line 228) | def _shutdown(self):
    method close (line 234) | def close(self):
    method select (line 241) | def select(self, mailbox='INBOX', readonly=False):
    method mailbox_info (line 266) | def mailbox_info(self, k, default=None):
    method _update_name (line 271) | def _update_name(self):
    method __enter__ (line 278) | def __enter__(self):
    method __exit__ (line 286) | def __exit__(self, type, value, traceback):
    method _start_idling (line 290) | def _start_idling(self):
    method _stop_idling (line 293) | def _stop_idling(self):
    method _imap_idle (line 296) | def _imap_idle(self):
    method quit (line 336) | def quit(self):
    method run (line 350) | def run(self):
  class SharedImapMailbox (line 377) | class SharedImapMailbox(Mailbox):
    method __init__ (line 387) | def __init__(self, session, mail_source,
    method open_imap (line 399) | def open_imap(self):
    method timed_imap (line 402) | def timed_imap(self, *args, **kwargs):
    method last_updated (line 405) | def last_updated(self):
    method _assert (line 408) | def _assert(self, test, error):
    method __nonzero__ (line 412) | def __nonzero__(self):
    method add (line 423) | def add(self, message):
    method remove (line 432) | def remove(self, key):
    method mailbox_info (line 443) | def mailbox_info(self, k, default=None):
    method get_info (line 450) | def get_info(self, key):
    method get (line 471) | def get(self, key, _bytes=None):
    method get_message (line 510) | def get_message(self, key):
    method get_bytes (line 514) | def get_bytes(self, key, *args):
    method get_file (line 518) | def get_file(self, key):
    method iterkeys (line 522) | def iterkeys(self):
    method keys (line 533) | def keys(self):
    method update_toc (line 536) | def update_toc(self):
    method get_msg_ptr (line 539) | def get_msg_ptr(self, mboxid, key):
    method get_file_by_ptr (line 542) | def get_file_by_ptr(self, msg_ptr):
    method remove_by_ptr (line 545) | def remove_by_ptr(self, msg_ptr):
    method get_msg_size (line 548) | def get_msg_size(self, key):
    method get_metadata_keywords (line 551) | def get_metadata_keywords(self, key):
    method __contains__ (line 564) | def __contains__(self, key):
    method __len__ (line 571) | def __len__(self):
    method flush (line 578) | def flush(self):
    method close (line 581) | def close(self):
    method lock (line 584) | def lock(self):
    method unlock (line 587) | def unlock(self):
    method save (line 590) | def save(self, *args, **kwargs):
    method get_index (line 594) | def get_index(self, config, mbx_mid=None):
    method __unicode__ (line 599) | def __unicode__(self):
    method describe_msg_by_ptr (line 605) | def describe_msg_by_ptr(self, msg_ptr):
  function _connect_imap (line 612) | def _connect_imap(session, settings, event,
  class ImapMailSource (line 780) | class ImapMailSource(BaseMailSource):
    class MailSourceVfs (line 795) | class MailSourceVfs(BaseMailSource.MailSourceVfs):
      method _imap_path (line 797) | def _imap_path(self, path):
      method _imap (line 802) | def _imap(self, *args, **kwargs):
      method listdir_ (line 805) | def listdir_(self, where, **kwargs):
      method getflags_ (line 824) | def getflags_(self, fp, cfg):
      method abspath_ (line 835) | def abspath_(self, fp):
      method display_name_ (line 838) | def display_name_(self, fp, config):
      method isdir_ (line 841) | def isdir_(self, fp):
      method getsize_ (line 847) | def getsize_(self, path):
    method __init__ (line 851) | def __init__(self, *args, **kwargs):
    method Tester (line 864) | def Tester(cls, conn_cls, *args, **kwargs):
    method timed (line 868) | def timed(self, *args, **kwargs):
    method timed_imap (line 871) | def timed_imap(self, *args, **kwargs):
    method _conn_id (line 874) | def _conn_id(self):
    method close (line 883) | def close(self):
    method open (line 893) | def open(self, conn_cls=None, throw=False):
    method _idle_callback (line 972) | def _idle_callback(self, data):
    method _check_keepalive (line 980) | def _check_keepalive(self):
    method open_mailbox (line 987) | def open_mailbox(self, mbx_id, mfn):
    method _get_mbx_id_and_mfn (line 996) | def _get_mbx_id_and_mfn(self, mbx_cfg):
    method _has_mailbox_changed (line 1000) | def _has_mailbox_changed(self, mbx_cfg, state):
    method _mark_mailbox_rescanned (line 1010) | def _mark_mailbox_rescanned(self, mbx, state):
    method _namespace_info (line 1017) | def _namespace_info(self, path):
    method _default_policy (line 1027) | def _default_policy(self, mbx_cfg):
    method _sorted_mailboxes (line 1034) | def _sorted_mailboxes(self):
    method _msg_key_order (line 1041) | def _msg_key_order(self, key):
    method _strip_file_extension (line 1044) | def _strip_file_extension(self, mbx_path):
    method _decode_path (line 1047) | def _decode_path(self, path):
    method _mailbox_path (line 1053) | def _mailbox_path(self, mbx_path):
    method _mailbox_path_split (line 1057) | def _mailbox_path_split(self, mbx_path):
    method _mailbox_name (line 1062) | def _mailbox_name(self, mbx_path):
    method _fmt_path (line 1067) | def _fmt_path(self, path):
    method _fix_empty_discovery_path_bug (line 1070) | def _fix_empty_discovery_path_bug(self):
    method discover_mailboxes (line 1075) | def discover_mailboxes(self, paths=None):
    method _cache_flags (line 1109) | def _cache_flags(self, path, flags=None):
    method _walk_mailbox_path (line 1115) | def _walk_mailbox_path(self, conn, prefix):
    method quit (line 1151) | def quit(self, *args, **kwargs):
  function TestImapSettings (line 1157) | def TestImapSettings(session, settings, event,
  class _MockImap (line 1172) | class _MockImap(object):
    method __init__ (line 1193) | def __init__(self, *args, **kwargs):
    method __getattr__ (line 1203) | def __getattr__(self, attr):
  class _Mocks (line 1207) | class _Mocks(object):
    class NoDns (line 1217) | class NoDns(_MockImap):
      method __init__ (line 1218) | def __init__(self, *args, **kwargs):
    class BadLogin (line 1221) | class BadLogin(_MockImap):

FILE: mailpile/mail_source/imap_starttls.py
  class IMAP4 (line 10) | class IMAP4(imaplib.IMAP4, object):
    method readline (line 19) | def readline(self):
    method starttls (line 26) | def starttls(self, keyfile = None, certfile = None):

FILE: mailpile/mail_source/imap_utf7.py
  function modified_base64 (line 10) | def modified_base64 (s):
  function doB64 (line 14) | def doB64(_in, r):
  function encoder (line 19) | def encoder(s):
  function modified_unbase64 (line 36) | def modified_unbase64(s):
  function decoder (line 40) | def decoder (s):
  class StreamReader (line 62) | class StreamReader (codecs.StreamReader):
    method decode (line 63) | def decode (self, s, errors='strict'):
  class StreamWriter (line 66) | class StreamWriter (codecs.StreamWriter):
    method decode (line 67) | def decode (self, s, errors='strict'):
  function imap4_utf_7 (line 70) | def imap4_utf_7(name):

FILE: mailpile/mail_source/local.py
  class LocalMailSource (line 10) | class LocalMailSource(BaseMailSource):
    method __init__ (line 17) | def __init__(self, *args, **kwargs):
    method _sleeping_is_ok (line 26) | def _sleeping_is_ok(self, slept):
    method close (line 44) | def close(self):
    method open (line 47) | def open(self):
    method _get_macmaildir_data (line 63) | def _get_macmaildir_data(self, path):
    method _data_paths (line 68) | def _data_paths(self, mbx):
    method _mailbox_sort_key (line 85) | def _mailbox_sort_key(self, mbx):
    method _has_mailbox_changed (line 98) | def _has_mailbox_changed(self, mbx, state):
    method _mark_mailbox_rescanned (line 122) | def _mark_mailbox_rescanned(self, mbx, state):
    method _is_mbox (line 128) | def _is_mbox(self, fn):
    method _is_maildir (line 150) | def _is_maildir(self, fn):
    method _is_macmaildir (line 159) | def _is_macmaildir(self, path):
    method is_mailbox (line 166) | def is_mailbox(self, fn):

FILE: mailpile/mail_source/pop3.py
  function _open_pop3_mailbox (line 19) | def _open_pop3_mailbox(session, event, host, port,
  class POP3_IOError (line 64) | class POP3_IOError(IOError):
  class Pop3MailSource (line 68) | class Pop3MailSource(BaseMailSource):
    method __init__ (line 75) | def __init__(self, *args, **kwargs):
    method close (line 79) | def close(self):
    method _sleep (line 89) | def _sleep(self, *args, **kwargs):
    method open (line 93) | def open(self):
    method _has_mailbox_changed (line 112) | def _has_mailbox_changed(self, mbx, state):
    method _mark_mailbox_rescanned (line 119) | def _mark_mailbox_rescanned(self, mbx, state):
    method _fmt_path (line 125) | def _fmt_path(self):
    method open_mailbox (line 128) | def open_mailbox(self, mbx_id, mfn):
    method discover_mailboxes (line 141) | def discover_mailboxes(self, paths=None):
    method is_mailbox (line 150) | def is_mailbox(self, fn):
    method _mailbox_name (line 153) | def _mailbox_name(self, path):
    method _create_tag (line 156) | def _create_tag(self, *args, **kwargs):
  function TestPop3Settings (line 166) | def TestPop3Settings(session, settings, event):

FILE: mailpile/mailboxes/__init__.py
  class NoSuchMailboxError (line 28) | class NoSuchMailboxError(OSError):
  function register (line 32) | def register(prio, cls):
  function IsMailbox (line 38) | def IsMailbox(fn, config):
  function OpenMailbox (line 50) | def OpenMailbox(fn, config, create=False):
  function UnorderedPicklable (line 62) | def UnorderedPicklable(parent, editable=False):

FILE: mailpile/mailboxes/gmvault.py
  class MailpileMailbox (line 12) | class MailpileMailbox(maildir.MailpileMailbox):
    method parse_path (line 16) | def parse_path(cls, config, fn, create=False, allow_empty=False):
    method __init__ (line 24) | def __init__(self, dirname, factory=rfc822.Message, create=True):
    method get_file (line 29) | def get_file(self, key):
    method _refresh (line 38) | def _refresh(self):

FILE: mailpile/mailboxes/macmail.py
  class _MacMaildirPartialFile (line 15) | class _MacMaildirPartialFile(mailbox._PartialFile):
    method __init__ (line 16) | def __init__(self, fd):
  class MacMaildirMessage (line 23) | class MacMaildirMessage(mailbox.Message):
    method __init__ (line 24) | def __init__(self, message=None):
  class MacMaildir (line 32) | class MacMaildir(mailbox.Mailbox):
    method __init__ (line 33) | def __init__(self, dirname, factory=rfc822.Message, create=True):
    method remove (line 67) | def remove(self, key):
    method discard (line 75) | def discard(self, key):
    method __setitem__ (line 85) | def __setitem__(self, key, message):
    method iterkeys (line 89) | def iterkeys(self):
    method has_key (line 98) | def has_key(self, key):
    method __len__ (line 102) | def __len__(self):
    method _refresh (line 106) | def _refresh(self):
    method _lookup (line 123) | def _lookup(self, key):
    method get_message (line 135) | def get_message(self, key):
    method get_file (line 141) | def get_file(self, key):
  class MailpileMailbox (line 146) | class MailpileMailbox(UnorderedPicklable(MacMaildir)):
    method parse_path (line 149) | def parse_path(cls, config, fn, create=False, allow_empty=False):
    method __unicode__ (line 155) | def __unicode__(self):
    method _describe_msg_by_ptr (line 158) | def _describe_msg_by_ptr(self, msg_ptr):

FILE: mailpile/mailboxes/maildir.py
  class MailpileMailbox (line 11) | class MailpileMailbox(UnorderedPicklable(mailbox.Maildir, editable=True)):
    method parse_path (line 16) | def parse_path(cls, config, fn, create=False, allow_empty=False):
    method _refresh (line 25) | def _refresh(self):
    method __unicode__ (line 32) | def __unicode__(self):
    method _describe_msg_by_ptr (line 35) | def _describe_msg_by_ptr(self, msg_ptr):
    method get_metadata_keywords (line 38) | def get_metadata_keywords(self, toc_id):

FILE: mailpile/mailboxes/maildirwin.py
  class MailpileMailbox (line 8) | class MailpileMailbox(maildir.MailpileMailbox):

FILE: mailpile/mailboxes/mbox.py
  class MboxIndex (line 17) | class MboxIndex(MailboxIndex):
  class MailpileMailbox (line 21) | class MailpileMailbox(mailbox.mbox):
    method parse_path (line 27) | def parse_path(cls, config, fn, create=False, allow_empty=False):
    method __init__ (line 39) | def __init__(self, *args, **kwargs):
    method __enter__ (line 52) | def __enter__(self, *args, **kwargs):
    method __exit__ (line 57) | def __exit__(self, *args, **kwargs):
    method __unicode__ (line 61) | def __unicode__(self):
    method describe_msg_by_ptr (line 64) | def describe_msg_by_ptr(self, msg_ptr):
    method _get_fd (line 71) | def _get_fd(self):
    method __setstate__ (line 74) | def __setstate__(self, dict):
    method __getstate__ (line 95) | def __getstate__(self):
    method last_updated (line 105) | def last_updated(self):
    method keys (line 108) | def keys(self):
    method toc_values (line 112) | def toc_values(self):
    method update_toc (line 116) | def update_toc(self):
    method _generate_toc (line 165) | def _generate_toc(self):
    method __setitem__ (line 168) | def __setitem__(self, *args, **kwargs):
    method __delitem__ (line 172) | def __delitem__(self, *args, **kwargs):
    method save (line 176) | def save(self, session=None, to=None, pickler=None):
    method _locked_flush_without_tempfile (line 186) | def _locked_flush_without_tempfile(self):
    method flush (line 205) | def flush(self, *args, **kwargs):
    method clear (line 220) | def clear(self, *args, **kwargs):
    method get_msg_size (line 224) | def get_msg_size(self, toc_id):
    method get_metadata_keywords (line 235) | def get_metadata_keywords(self, toc_id):
    method set_metadata_keywords (line 239) | def set_metadata_keywords(self, *args, **kwargs):
    method get_index (line 242) | def get_index(self, config, mbx_mid=None):
    method get_msg_cs (line 248) | def get_msg_cs(self, start, cs_size, max_length, chars=4, data=None):
    method get_msg_cs4k (line 265) | def get_msg_cs4k(self, start, max_length, data=None):
    method get_msg_cs80b (line 269) | def get_msg_cs80b(self, start, max_length, data=None):
    method get_msg_ptr (line 273) | def get_msg_ptr(self, mboxid, toc_id, data=None):
    method _parse_ptr (line 284) | def _parse_ptr(self, msg_ptr):
    method _verify_ptr_checksums (line 294) | def _verify_ptr_checksums(self, msg_ptr, start, ignored_fd):
    method _possible_message_locations (line 306) | def _possible_message_locations(self, msg_ptr, max_locations=15):
    method _get_SSLP_by_ptr (line 325) | def _get_SSLP_by_ptr(self, msg_ptr, verifier=None, from_=False):
    method update (line 352) | def update(self, *args, **kwargs):
    method discard (line 357) | def discard(self, *args, **kwargs):
    method remove (line 362) | def remove(self, *args, **kwargs):
    method get_file_by_ptr (line 367) | def get_file_by_ptr(self, msg_ptr, verifier=None, from_=False):
    method remove_by_ptr (line 373) | def remove_by_ptr(self, msg_ptr):
    method get_bytes (line 381) | def get_bytes(self, toc_id, *args, **kwargs):
    method get_file (line 385) | def get_file(self, *args, **kwargs):

FILE: mailpile/mailboxes/pop3.py
  class wrappable_POP3_SSL (line 21) | class wrappable_POP3_SSL(poplib.POP3_SSL):
    method __init__ (line 25) | def __init__(self, host,
  class UnsupportedProtocolError (line 40) | class UnsupportedProtocolError(Exception):
  class POP3Mailbox (line 44) | class POP3Mailbox(Mailbox):
    method __init__ (line 48) | def __init__(self, host,
    method lock (line 68) | def lock(self):
    method unlock (line 71) | def unlock(self):
    method _connect (line 74) | def _connect(self):
    method _refresh (line 118) | def _refresh(self):
    method __setitem__ (line 123) | def __setitem__(self, key, message):
    method _get (line 127) | def _get(self, key, _bytes=None):
    method get_message (line 165) | def get_message(self, key):
    method get_bytes (line 169) | def get_bytes(self, key, *args):
    method get_file (line 173) | def get_file(self, key):
    method get_msg_size (line 177) | def get_msg_size(self, key):
    method remove (line 185) | def remove(self, key):
    method stat (line 195) | def stat(self):
    method iterkeys (line 200) | def iterkeys(self):
    method __contains__ (line 214) | def __contains__(self, key):
    method __len__ (line 218) | def __len__(self):
    method flush (line 222) | def flush(self):
    method close (line 226) | def close(self):
  class MailpileMailbox (line 236) | class MailpileMailbox(UnorderedPicklable(POP3Mailbox)):
    method parse_path (line 240) | def parse_path(cls, config, path, create=False, allow_empty=False):
    method save (line 269) | def save(self, *args, **kwargs):
  class _MockPOP3 (line 280) | class _MockPOP3(object):
    method __init__ (line 341) | def __init__(self, *args, **kwargs):
    method list (line 359) | def list(self, which=None):
    method __getattr__ (line 365) | def __getattr__(self, attr):
  class _MockPOP3_Without_UIDL (line 368) | class _MockPOP3_Without_UIDL(_MockPOP3):

FILE: mailpile/mailboxes/wervd.py
  class MailpileMailbox (line 15) | class MailpileMailbox(UnorderedPicklable(mailbox.Maildir, editable=True)):
    method parse_path (line 30) | def parse_path(cls, config, fn, create=False, allow_empty=False):
    method __init2__ (line 40) | def __init2__(self, *args, **kwargs):
    method __unicode__ (line 43) | def __unicode__(self):
    method _describe_msg_by_ptr (line 46) | def _describe_msg_by_ptr(self, msg_ptr):
    method remove (line 54) | def remove(self, key):
    method _refresh (line 70) | def _refresh(self):
    method _get_fd (line 78) | def _get_fd(self, key):
    method get_message (line 88) | def get_message(self, key):
    method get_string (line 97) | def get_string(self, key):
    method get_file (line 102) | def get_file(self, key):
    method get_metadata_keywords (line 106) | def get_metadata_keywords(self, toc_id):
    method set_metadata_keywords (line 114) | def set_metadata_keywords(self, toc_id, kws):
    method add (line 127) | def add(self, message):
    method _dump_message (line 170) | def _dump_message(self, message, target):
    method __setitem__ (line 179) | def __setitem__(self, key, message):

FILE: mailpile/mailutils/__init__.py
  function FormatMbxId (line 6) | def FormatMbxId(n):
  class NotEditableError (line 14) | class NotEditableError(ValueError):
  class NoFromAddressError (line 18) | class NoFromAddressError(ValueError):
  class NoRecipientError (line 22) | class NoRecipientError(ValueError):
  class InsecureSmtpError (line 26) | class InsecureSmtpError(IOError):
    method __init__ (line 27) | def __init__(self, msg, details=None):
  class NoSuchMailboxError (line 32) | class NoSuchMailboxError(OSError):

FILE: mailpile/mailutils/addresses.py
  class AddressHeaderParser (line 16) | class AddressHeaderParser(list):
    method __init__ (line 149) | def __init__(self,
    method _reset (line 161) | def _reset(self, _raw_data=None, strict=False, _raise=False):
    method parse (line 167) | def parse(self, data):
    method _parse (line 170) | def _parse(self, data, strict=False, _raise=False):
    method unquote (line 200) | def unquote(self, string, charset_order=None):
    method unescape (line 225) | def unescape(self, string):
    method escape (line 229) | def escape(self, strng):
    method quote (line 233) | def quote(self, strng):
    method _tokenize (line 243) | def _tokenize(self, string, munge=False):
    method _clean (line 252) | def _clean(self, token):
    method _group (line 263) | def _group(self, tokens, munge=False):
    method _find_addresses (line 285) | def _find_addresses(self, groups, **fa_kwargs):
    method _find_address (line 289) | def _find_address(self, g, _raise=False, munge=False):
    method addresses_list (line 346) | def addresses_list(self, with_keys=False):
    method normalized_addresses (line 355) | def normalized_addresses(self,
    method normalized (line 377) | def normalized(self, **kwargs):

FILE: mailpile/mailutils/emails.py
  function MakeContentID (line 50) | def MakeContentID():
  function MakeBoundary (line 58) | def MakeBoundary():
  function MakeMessageID (line 62) | def MakeMessageID():
  function MakeMessageDate (line 70) | def MakeMessageDate(ts=None):
  function ClearParseCache (line 82) | def ClearParseCache(cache_id=None, pgpmime=False, full=False):
  function ParseMessage (line 93) | def ParseMessage(fd, cache_id=None, update_cache=False,
  function GetTextPayload (line 151) | def GetTextPayload(part):
  function ExtractEmails (line 168) | def ExtractEmails(string, strip_keys=True):
  function ExtractEmailAndName (line 194) | def ExtractEmailAndName(string):
  function CleanHeaders (line 205) | def CleanHeaders(msg, copy_all=True, tombstones=False):
  function CleanMessage (line 230) | def CleanMessage(config, msg):
  function PrepareMessage (line 242) | def PrepareMessage(config, msg,
  class Email (line 336) | class Email(object):
    method __init__ (line 339) | def __init__(self, idx, msg_idx_pos,
    method msg_mid (line 351) | def msg_mid(self):
    method encoded_hdr (line 355) | def encoded_hdr(self, msg, hdr, value=None):
    method Create (line 384) | def Create(cls, idx, mbox_id, mbx,
    method is_editable (line 498) | def is_editable(self, quick=False):
    method _attachment_aid (line 526) | def _attachment_aid(self, att):
    method get_editing_strings (line 539) | def get_editing_strings(self, tree=None, build_tree=True):
    method get_editing_string (line 592) | def get_editing_string(self, tree=None,
    method _update_att_name (line 612) | def _update_att_name(self, part, filename):
    method _make_attachment (line 621) | def _make_attachment(self, fn, msg, filedata=None):
    method update_from_string (line 652) | def update_from_string(self, session, data, final=False):
    method update_from_msg (line 730) | def update_from_msg(self, session, newmsg):
    method reset_caches (line 762) | def reset_caches(self,
    method update_parse_cache (line 772) | def update_parse_cache(self, newmsg):
    method clear_from_parse_cache (line 781) | def clear_from_parse_cache(self):
    method delete_message (line 786) | def delete_message(self, session, flush=True, keep=0):
    method get_msg_info (line 825) | def get_msg_info(self, field=None, uncached=False):
    method get_mbox_ptr_and_fd (line 833) | def get_mbox_ptr_and_fd(self):
    method get_file (line 841) | def get_file(self):
    method get_msg_size (line 844) | def get_msg_size(self):
    method get_metadata_kws (line 850) | def get_metadata_kws(self):
    method get_cache_id (line 854) | def get_cache_id(self):
    method _get_parsed_msg (line 860) | def _get_parsed_msg(self, pgpmime, update_cache=False):
    method _update_crypto_state (line 874) | def _update_crypto_state(self):
    method get_msg (line 914) | def get_msg(self, pgpmime='default', crypto_state_feedback=True):
    method is_thread (line 937) | def is_thread(self):
    method get (line 941) | def get(self, field, default=''):
    method get_sender (line 952) | def get_sender(self):
    method get_headerprints (line 959) | def get_headerprints(self):
    method get_msg_summary (line 962) | def get_msg_summary(self):
    method _find_attachments (line 977) | def _find_attachments(self, att_id, negative=False):
    method add_attachments (line 1001) | def add_attachments(self, session, filenames, filedata=None):
    method remove_attachments (line 1011) | def remove_attachments(self, session, *att_ids):
    method extract_attachment (line 1034) | def extract_attachment(self, session, att_id, name_fmt=None, mode='get'):
    method get_message_tags (line 1104) | def get_message_tags(self):
    method get_message_tree (line 1108) | def get_message_tree(self, want=None, tree=None, pgpmime='default'):
    method decode_text (line 1279) | def decode_text(self, payload, charset='utf-8', binary=True):
    method decode_payload (line 1298) | def decode_payload(self, part):
    method parse_text_part (line 1302) | def parse_text_part(self, data, charset, crypto, mimetype, count):
    method parse_line_type (line 1349) | def parse_line_type(self, line, block, line_count):
    method evaluate_pgp (line 1428) | def evaluate_pgp(self, tree, check_sigs=True, decrypt=False,
    method _decode_gpg (line 1501) | def _decode_gpg(self, message, decrypted):

FILE: mailpile/mailutils/generator.py
  function _is8bitstring (line 81) | def _is8bitstring(s):
  class Generator (line 90) | class Generator:
    method __init__ (line 100) | def __init__(self, outfp,
    method write (line 123) | def write(self, s):
    method flatten (line 127) | def flatten(self, msg, unixfrom=False, linesep=None):
    method clone (line 150) | def clone(self, fp):
    method _write (line 158) | def _write(self, msg):
    method _dispatch (line 185) | def _dispatch(self, msg):
    method _write_headers (line 205) | def _write_headers(self, msg):
    method _handle_text (line 239) | def _handle_text(self, msg):
    method _handle_multipart (line 252) | def _handle_multipart(self, msg):
    method _handle_multipart_signed (line 310) | def _handle_multipart_signed(self, msg):
    method _handle_message_delivery_status (line 325) | def _handle_message_delivery_status(self, msg):
    method _handle_message (line 346) | def _handle_message(self, msg):
  class DecodedGenerator (line 368) | class DecodedGenerator(Generator):
    method __init__ (line 374) | def __init__(self, outfp,
    method _dispatch (line 403) | def _dispatch(self, msg):
  function _make_boundary (line 429) | def _make_boundary(text=None):

FILE: mailpile/mailutils/header.py
  function decode_header (line 28) | def decode_header(header):

FILE: mailpile/mailutils/headerprint.py
  function HeaderPrintMTADetails (line 35) | def HeaderPrintMTADetails(message):
  function HeaderPrintMUADetails (line 60) | def HeaderPrintMUADetails(message, mta=None):
  function HeaderPrintGenericDetails (line 85) | def HeaderPrintGenericDetails(message, which=MUA_HP_HEADERS):
  function HeaderPrints (line 90) | def HeaderPrints(message):

FILE: mailpile/mailutils/html.py
  function clean_html (line 55) | def clean_html(html):
  function extract_text_from_html (line 63) | def extract_text_from_html(html, url_callback=None):

FILE: mailpile/mailutils/safe.py
  function safe_decode_hdr (line 17) | def safe_decode_hdr(msg=None, name=None, hdr=None, charset=None):
  function safe_parse_date (line 85) | def safe_parse_date(date_hdr):
  function safe_message_ts (line 98) | def safe_message_ts(msg, default=None, msg_mid=None, msg_id=None, sessio...
  function safe_get_msg_id (line 149) | def safe_get_msg_id(msg):

FILE: mailpile/mailutils/vcal.py
  function calendar_parse (line 6) | def calendar_parse(payload):
  class VObject (line 36) | class VObject:
    method __init__ (line 37) | def __init__(self):
    method add_part (line 43) | def add_part(self, key, params, value):
    method find_parts (line 46) | def find_parts(self, key):
    method find_one_part (line 53) | def find_one_part(self, key):
    method find_one_part_value (line 59) | def find_one_part_value(self, key, value=None):
    method get_datetime (line 64) | def get_datetime(self, key):
    method to_raw_json (line 71) | def to_raw_json(self):
    method to_json (line 85) | def to_json(self):
  class VTimeZone (line 88) | class VTimeZone(VObject):
  class VTZStandard (line 91) | class VTZStandard(VObject):
  class VTZDaylight (line 94) | class VTZDaylight(VObject):
  class VAlarm (line 97) | class VAlarm(VObject):
  class VEvent (line 100) | class VEvent(VObject):
    method __init__ (line 101) | def __init__(self):
    method to_json (line 104) | def to_json(self):
  class VCalendar (line 128) | class VCalendar(VObject):
    method __init__ (line 129) | def __init__(self):
    method print_events (line 132) | def print_events(self):
    method to_json (line 140) | def to_json(self):

FILE: mailpile/packing.py
  function PackIntSet (line 9) | def PackIntSet(ints):
  function UnpackIntSet (line 41) | def UnpackIntSet(data):
  function PackLongList (line 51) | def PackLongList(longs):
  function UnpackLongList (line 85) | def UnpackLongList(data):
  class StorageBackedData (line 94) | class StorageBackedData(object):
    method __init__ (line 106) | def __init__(self, storage, skey):
    method _pack (line 115) | def _pack(self, data): raise NotImplemented()
    method _unpack (line 116) | def _unpack(self, data): raise NotImplemented()
    method load (line 118) | def load(self):
    method save (line 124) | def save(self, maybe=False):
    method _dirty_maybe_save (line 129) | def _dirty_maybe_save(self):
    method _r (line 136) | def _r(self, method, *args, **kwargs):
    method _w (line 139) | def _w(self, method, *args, **kwargs):
    method _iw (line 144) | def _iw(self, method, *args, **kwargs):
    method __and__ (line 149) | def __and__(s, *a, **kw): return s._r('__and__', *a, **kw)
    method __cmp__ (line 150) | def __cmp__(s, *a, **kw): return s._r('__cmp__', *a, **kw)
    method __contains__ (line 151) | def __contains__(s, *a, **kw): return s._r('__contains__', *a, **kw)
    method __eq__ (line 152) | def __eq__(s, *a, **kw): return s._r('__eq__', *a, **kw)
    method __ge__ (line 153) | def __ge__(s, *a, **kw): return s._r('__ge__', *a, **kw)
    method __getitem__ (line 154) | def __getitem__(s, *a, **kw): return s._r('__getitem__', *a, **kw)
    method __getslice__ (line 155) | def __getslice__(s, *a, **kw): return s._r('__getslice__', *a, **kw)
    method __gt__ (line 156) | def __gt__(s, *a, **kw): return s._r('__gt__', *a, **kw)
    method __iter__ (line 157) | def __iter__(s, *a, **kw): return s._r('__iter__', *a, **kw)
    method __le__ (line 158) | def __le__(s, *a, **kw): return s._r('__le__', *a, **kw)
    method __len__ (line 159) | def __len__(s, *a, **kw): return s._r('__len__', *a, **kw)
    method __lt__ (line 160) | def __lt__(s, *a, **kw): return s._r('__lt__', *a, **kw)
    method __mul__ (line 161) | def __mul__(s, *a, **kw): return s._r('__mul__', *a, **kw)
    method __ne__ (line 162) | def __ne__(s, *a, **kw): return s._r('__ne__', *a, **kw)
    method __or__ (line 163) | def __or__(s, *a, **kw): return s._r('__or__', *a, **kw)
    method __rand__ (line 164) | def __rand__(s, *a, **kw): return s._r('__rand__', *a, **kw)
    method __reduce__ (line 165) | def __reduce__(s, *a, **kw): return s._r('__reduce__', *a, **kw)
    method __repr__ (line 166) | def __repr__(s, *a, **kw): return s._r('__repr__', *a, **kw)
    method __reversed__ (line 167) | def __reversed__(s, *a, **kw): return s._r('__reversed__', *a, **kw)
    method __rmul__ (line 168) | def __rmul__(s, *a, **kw): return s._r('__rmul__', *a, **kw)
    method __rsub__ (line 169) | def __rsub__(s, *a, **kw): return s._r('__rsub__', *a, **kw)
    method __rxor__ (line 170) | def __rxor__(s, *a, **kw): return s._r('__rxor__', *a, **kw)
    method __sizeof__ (line 171) | def __sizeof__(s, *a, **kw): return s._r('__sizeof__', *a, **kw)
    method __sub__ (line 172) | def __sub__(s, *a, **kw): return s._r('__sub__', *a, **kw)
    method __xor__ (line 173) | def __xor__(s, *a, **kw): return s._r('__xor__', *a, **kw)
    method copy (line 174) | def copy(s, *a, **kw): return s._r('copy', *a, **kw)
    method count (line 175) | def count(s, *a, **kw): return s._r('count', *a, **kw)
    method difference (line 176) | def difference(s, *a, **kw): return s._r('difference', *a, **kw)
    method index (line 177) | def index(s, *a, **kw): return s._r('index', *a, **kw)
    method intersection (line 178) | def intersection(s, *a, **kw): return s._r('intersection', *a, **kw)
    method isdisjoint (line 179) | def isdisjoint(s, *a, **kw): return s._r('isdisjoint', *a, **kw)
    method issubset (line 180) | def issubset(s, *a, **kw): return s._r('issubset', *a, **kw)
    method issuperset (line 181) | def issuperset(s, *a, **kw): return s._r('issuperset', *a, **kw)
    method union (line 182) | def union(s, *a, **kw): return s._r('union', *a, **kw)
    method symmetric_difference (line 184) | def symmetric_difference(s, *a, **kw):
    method __iadd__ (line 187) | def __iadd__(s, *a, **kw): return s._iw('__iadd__', *a, **kw)
    method __iand__ (line 188) | def __iand__(s, *a, **kw): return s._iw('__iand__', *a, **kw)
    method __imul__ (line 189) | def __imul__(s, *a, **kw): return s._iw('__imul__', *a, **kw)
    method __ior__ (line 190) | def __ior__(s, *a, **kw): return s._iw('__ior__', *a, **kw)
    method __isub__ (line 191) | def __isub__(s, *a, **kw): return s._iw('__isub__', *a, **kw)
    method __ixor__ (line 192) | def __ixor__(s, *a, **kw): return s._iw('__ixor__', *a, **kw)
    method __delitem__ (line 194) | def __delitem__(s, *a, **kw): return s._w('__delitem__', *a, **kw)
    method __delslice__ (line 195) | def __delslice__(s, *a, **kw): return s._w('__delslice__', *a, **kw)
    method __setitem__ (line 196) | def __setitem__(s, *a, **kw): return s._w('__setitem__', *a, **kw)
    method __setslice__ (line 197) | def __setslice__(s, *a, **kw): return s._w('__setslice__', *a, **kw)
    method add (line 198) | def add(s, *a, **kw): return s._w('add', *a, **kw)
    method append (line 199) | def append(s, *a, **kw): return s._w('append', *a, **kw)
    method clear (line 200) | def clear(s, *a, **kw): return s._w('clear', *a, **kw)
    method discard (line 201) | def discard(s, *a, **kw): return s._w('discard', *a, **kw)
    method extend (line 202) | def extend(s, *a, **kw): return s._w('extend', *a, **kw)
    method insert (line 203) | def insert(s, *a, **kw): return s._w('insert', *a, **kw)
    method pop (line 204) | def pop(s, *a, **kw): return s._w('pop', *a, **kw)
    method remove (line 205) | def remove(s, *a, **kw): return s._w('remove', *a, **kw)
    method reverse (line 206) | def reverse(s, *a, **kw): return s._w('reverse', *a, **kw)
    method sort (line 207) | def sort(s, *a, **kw): return s._w('sort', *a, **kw)
    method update (line 208) | def update(s, *a, **kw): return s._w('update', *a, **kw)
    method difference_update (line 210) | def difference_update(s, *a, **kw):
    method intersection_update (line 212) | def intersection_update(s, *a, **kw):
    method symmetric_difference_update (line 214) | def symmetric_difference_update(s, *a, **kw):
  class StorageBackedSet (line 218) | class StorageBackedSet(StorageBackedData):
    method _pack (line 233) | def _pack(self, data): return PackIntSet(data)
    method _unpack (line 234) | def _unpack(self, data): return UnpackIntSet(data)
  class StorageBackedLongs (line 237) | class StorageBackedLongs(StorageBackedData):
    method _pack (line 252) | def _pack(self, data): return PackLongList(data)
    method _unpack (line 253) | def _unpack(self, data): return UnpackLongList(data)

FILE: mailpile/platforms.py
  function _assert_file_exists (line 27) | def _assert_file_exists(path):
  function DetectBinaries (line 33) | def DetectBinaries(
  function GetDefaultGnuPGCommand (line 104) | def GetDefaultGnuPGCommand(_raise=OSError):
  function GetDefaultOpenSSLCommand (line 108) | def GetDefaultOpenSSLCommand(_raise=OSError):
  function GetDefaultTorPath (line 112) | def GetDefaultTorPath(_raise=OSError):
  function InDesktopEnvironment (line 116) | def InDesktopEnvironment():
  function RenameCannotOverwrite (line 124) | def RenameCannotOverwrite():
  function NeedExplicitPortCheck (line 131) | def NeedExplicitPortCheck():
  function TerminalSupportsAnsiColors (line 138) | def TerminalSupportsAnsiColors():
  function WindowsPopenSemantics (line 145) | def WindowsPopenSemantics():
  function GetAppDataDirectory (line 152) | def GetAppDataDirectory():
  function RestrictReadAccess (line 164) | def RestrictReadAccess(path):
  function RandomListeningPort (line 175) | def RandomListeningPort(count=1, host='127.0.0.1'):

FILE: mailpile/plugins/__init__.py
  class EmailTransform (line 36) | class EmailTransform(object):
    method __init__ (line 38) | def __init__(self, config):
    method _get_sender_profile (line 41) | def _get_sender_profile(self, sender, kwargs):
    method _get_first_part (line 47) | def _get_first_part(self, msg, mimetype):
    method TransformIncoming (line 55) | def TransformIncoming(self, *args, **kwargs):
    method TransformOutgoing (line 58) | def TransformOutgoing(self, *args, **kwargs):
  class PluginError (line 62) | class PluginError(Exception):
  class PluginManager (line 66) | class PluginManager(object):
    method __init__ (line 95) | def __init__(self, plugin_name=None, builtin=False, deprecated=False,
    method _listdir (line 113) | def _listdir(self, path):
    method _uncomment (line 119) | def _uncomment(self, json_data):
    method discover (line 123) | def discover(self, paths, update=False):
    method available (line 155) | def available(self):
    method loadable (line 158) | def loadable(self):
    method loadable_early (line 161) | def loadable_early(self):
    method _import (line 165) | def _import(self, full_name, full_path):
    method _load (line 183) | def _load(self, plugin_name, process_manifest=False, config=None):
    method load (line 241) | def load(self, *args, **kwargs):
    method process_shutdown_hooks (line 248) | def process_shutdown_hooks(self):
    method process_manifests (line 263) | def process_manifests(self):
    method _mf_path (line 278) | def _mf_path(self, mf, *path):
    method _mf_iteritems (line 283) | def _mf_iteritems(self, mf, *path):
    method _get_method (line 286) | def _get_method(self, full_name, method):
    method _get_class (line 293) | def _get_class(self, full_name, class_name):
    method _process_manifest_pass_one (line 299) | def _process_manifest_pass_one(self, full_name,
    method _process_manifest_pass_two (line 345) | def _process_manifest_pass_two(self, full_name,
    method _process_startup_hooks (line 473) | def _process_startup_hooks(self, package,
    method _compat_check (line 484) | def _compat_check(self, strict=True):
    method _rhtf (line 498) | def _rhtf(self, kw_hash, term, function):
    method register_config_variables (line 506) | def register_config_variables(self, *args):
    method register_config_section (line 520) | def register_config_section(self, *args):
    method _txf_in (line 542) | def _txf_in(self, transforms, config, msg, kwargs):
    method _txf_out (line 553) | def _txf_out(self, transforms, cfg, s, r, msg, kwa):
    method incoming_email_crypto_transform (line 564) | def incoming_email_crypto_transform(self, cfg, msg, **kwa):
    method incoming_email_content_transform (line 567) | def incoming_email_content_transform(self, config, msg, **kwa):
    method outgoing_email_content_transform (line 570) | def outgoing_email_content_transform(self, cfg, s, r, m, **kwa):
    method outgoing_email_crypto_transform (line 573) | def outgoing_email_crypto_transform(self, cfg, s, r, m, **kwa):
    method register_incoming_email_crypto_transform (line 576) | def register_incoming_email_crypto_transform(self, name, transform):
    method register_incoming_email_content_transform (line 579) | def register_incoming_email_content_transform(self, name, transform):
    method register_outgoing_email_content_transform (line 582) | def register_outgoing_email_content_transform(self, name, transform):
    method register_outgoing_email_crypto_transform (line 585) | def register_outgoing_email_crypto_transform(self, name, transform):
    method register_data_kw_extractor (line 595) | def register_data_kw_extractor(self, term, function):
    method register_text_kw_extractor (line 599) | def register_text_kw_extractor(self, term, function):
    method register_meta_kw_extractor (line 603) | def register_meta_kw_extractor(self, term, function):
    method get_data_kw_extractors (line 607) | def get_data_kw_extractors(self):
    method get_text_kw_extractors (line 611) | def get_text_kw_extractors(self):
    method get_meta_kw_extractors (line 615) | def get_meta_kw_extractors(self):
    method get_search_term (line 624) | def get_search_term(self, term, default=None):
    method register_search_term (line 628) | def register_search_term(self, term, function):
    method get_filter_hooks (line 640) | def get_filter_hooks(self, hooks):
    method register_filter_hook_pre (line 648) | def register_filter_hook_pre(self, name, hook):
    method register_filter_hook_post (line 652) | def register_filter_hook_post(self, name, hook):
    method _reg_vcard_plugin (line 663) | def _reg_vcard_plugin(self, what, cfg_sect, plugin_classes, cls, dct):
    method register_vcard_importers (line 686) | def register_vcard_importers(self, *importers):
    method register_contact_exporters (line 692) | def register_contact_exporters(self, *exporters):
    method register_contact_context_providers (line 698) | def register_contact_context_providers(self, *providers):
    method register_fast_periodic_job (line 710) | def register_fast_periodic_job(self, name, period, callback):
    method register_slow_periodic_job (line 715) | def register_slow_periodic_job(self, name, period, callback):
    method register_worker (line 725) | def register_worker(self, thread_obj):
    method register_commands (line 735) | def register_commands(self, *args):
    method register_js (line 749) | def register_js(self, plugin, classname, filename):
    method register_css (line 752) | def register_css(self, plugin, classname, filename):
    method register_web_asset (line 755) | def register_web_asset(self, plugin, path, filename, mimetype='text/ht...
    method get_js_classes (line 760) | def get_js_classes(self):
    method get_css_files (line 763) | def get_css_files(self):
    method get_web_asset (line 766) | def get_web_asset(self, path, default=None):
    method register_ui_element (line 783) | def register_ui_element(self, ui_type,
    method get_ui_elements (line 807) | def get_ui_elements(self, ui_type, context):

FILE: mailpile/plugins/autotag.py
  function at_identify (line 45) | def at_identify(at_config):
  function autotag_configs (line 51) | def autotag_configs(config):
  class AutoTagger (line 74) | class AutoTagger(object):
    method __init__ (line 75) | def __init__(self, tagger, trainer):
    method reset (line 80) | def reset(self, at_config):
    method learn (line 85) | def learn(self, *args):
    method should_tag (line 89) | def should_tag(self, *args):
  function SaveAutoTagger (line 93) | def SaveAutoTagger(config, at_config):
  function LoadAutoTagger (line 100) | def LoadAutoTagger(config, at_config):
  class AutoTagCommand (line 127) | class AutoTagCommand(object):
    method __init__ (line 128) | def __init__(self, command):
    method _get_keywords (line 158) | def _get_keywords(self, e):
  class Tagger (line 132) | class Tagger(AutoTagCommand):
    method should_tag (line 133) | def should_tag(self, atagger, at_config, msg, keywords):
  class Trainer (line 138) | class Trainer(AutoTagCommand):
    method learn (line 139) | def learn(self, atagger, at_config, msg, keywords, should_tag):
    method reset (line 143) | def reset(self, atagger, at_config):
  class AutoTagCommand (line 155) | class AutoTagCommand(Command):
    method __init__ (line 128) | def __init__(self, command):
    method _get_keywords (line 158) | def _get_keywords(self, e):
  class Retrain (line 175) | class Retrain(AutoTagCommand):
    method command (line 178) | def command(self):
    method _retrain (line 181) | def _retrain(self, tags=None):
    method interval_retrain (line 309) | def interval_retrain(cls, session):
  class Classify (line 332) | class Classify(AutoTagCommand):
    method _classify (line 336) | def _classify(self, emails):
    method command (line 361) | def command(self):
  class AutoTag (line 368) | class AutoTag(Classify):
    method command (line 372) | def command(self):
  function filter_hook (line 409) | def filter_hook(session, msg_mid, msg, keywords, **kwargs):

FILE: mailpile/plugins/autotag_sb.py
  function _classifier (line 14) | def _classifier(autotagger):
  class SpamBayesTagger (line 20) | class SpamBayesTagger(mailpile.plugins.autotag.Trainer):
    method should_tag (line 21) | def should_tag(self, atagger, at_config, msg, keywords):
  class SpamBayesTrainer (line 33) | class SpamBayesTrainer(mailpile.plugins.autotag.Trainer):
    method learn (line 34) | def learn(self, atagger, at_config, msg, keywords, should_tag):
    method reset (line 37) | def reset(self, atagger, at_config):

FILE: mailpile/plugins/backups.py
  function _gzip (line 31) | def _gzip(filename, data):
  function _gunzip (line 39) | def _gunzip(data):
  function _decrypt (line 44) | def _decrypt(data, config):
  class MakeBackup (line 52) | class MakeBackup(Command):
    method SummarizeTags (line 61) | def SummarizeTags(cls, config):
    method MakeBackupArchive (line 92) | def MakeBackupArchive(cls, config, gnupg, what=None):
    method command (line 183) | def command(self):
  class RestoreBackup (line 215) | class RestoreBackup(Command):
    method _restore_PGP_keys (line 232) | def _restore_PGP_keys(self, config, backup_zip, policy):
    method _adjust_paths (line 247) | def _adjust_paths(self, config):
    method command (line 275) | def command(self):

FILE: mailpile/plugins/compose.py
  class EditableSearchResults (line 34) | class EditableSearchResults(SearchResults):
    method __init__ (line 35) | def __init__(self, session, idx, new, sent, **kwargs):
  function AddComposeMethods (line 46) | def AddComposeMethods(cls):
  class CompositionCommand (line 148) | class CompositionCommand(AddComposeMethods(Search)):
    method _new_msgid (line 167) | def _new_msgid(self):
    method _get_email_updates (line 173) | def _get_email_updates(self, idx, create=False, noneok=False, emails=N...
    method _return_search_results (line 248) | def _return_search_results(self, message, emails,
    method _edit_messages (line 268) | def _edit_messages(self, *args, **kwargs):
    method _real_edit_messages (line 274) | def _real_edit_messages(self, emails, new=True, tag=True, ephemeral=Fa...
  class Draft (line 292) | class Draft(AddComposeMethods(View)):
    method _side_effects (line 300) | def _side_effects(self, emails):
  class Compose (line 316) | class Compose(CompositionCommand):
    method _get_canned (line 326) | def _get_canned(cls, idx, cid):
    method CreateMessage (line 335) | def CreateMessage(cls, idx, session, msgid, cid=None, ephemeral=False):
    method command (line 351) | def command(self):
  class RelativeCompose (line 380) | class RelativeCompose(Compose):
    method prefix_subject (line 389) | def prefix_subject(subject, prefix, prefix_regex):
  class Reply (line 399) | class Reply(RelativeCompose):
    method _add_gpg_key (line 412) | def _add_gpg_key(cls, idx, session, addr):
    method _create_from_to_cc (line 423) | def _create_from_to_cc(cls, idx, session, trees):
    method CreateReply (line 481) | def CreateReply(cls, idx, session, refs, msgid,
    method command (line 553) | def command(self):
  class Forward (line 597) | class Forward(RelativeCompose):
    method CreateForward (line 610) | def CreateForward(cls, idx, session, refs, msgid,
    method command (line 658) | def command(self):
  class Attach (line 703) | class Attach(CompositionCommand):
    method command (line 716) | def command(self, emails=None):
  class UnAttach (line 783) | class UnAttach(CompositionCommand):
    method command (line 795) | def command(self, emails=None):
  class Sendit (line 849) | class Sendit(CompositionCommand):
    method command (line 865) | def command(self, emails=None):
  class Update (line 990) | class Update(CompositionCommand):
    method command (line 999) | def command(self, create=True, outbox=False):
  class UpdateAndSendit (line 1047) | class UpdateAndSendit(Update):
    method command (line 1051) | def command(self, create=True, outbox=True):
  class UnThread (line 1055) | class UnThread(CompositionCommand):
    method command (line 1064) | def command(self):
  class EmptyOutbox (line 1095) | class EmptyOutbox(Sendit):
    method sendmail (line 1101) | def sendmail(cls, session):
    method command (line 1104) | def command(self):

FILE: mailpile/plugins/contacts.py
  class VCardCommand (line 28) | class VCardCommand(Command):
    class CommandResult (line 33) | class CommandResult(Command.CommandResult):
      method as_text (line 36) | def as_text(self):
      method _as_text (line 42) | def _as_text(self):
      method _vcards_as_text (line 51) | def _vcards_as_text(self, result):
    method _form_defaults (line 83) | def _form_defaults(self):
    method _make_new_vcard (line 86) | def _make_new_vcard(self, handle, name, note, kind):
    method _valid_vcard_handle (line 99) | def _valid_vcard_handle(self, vc_handle):
    method _pre_delete_vcard (line 102) | def _pre_delete_vcard(self, vcard):
    method _vcard_list (line 105) | def _vcard_list(self, vcards, mode='mpCard', info=None, simplify=False):
  class VCard (line 139) | class VCard(VCardCommand):
    method command (line 145) | def command(self, save=True):
  class AddVCard (line 159) | class AddVCard(VCardCommand):
    method _add_from_messages (line 178) | def _add_from_messages(self, args, add_recipients):
    method _sanity_check (line 195) | def _sanity_check(self, kind, vcard):
    method _before_vcard_create (line 198) | def _before_vcard_create(self, kind, triplets):
    method _after_vcard_create (line 201) | def _after_vcard_create(self, kind, vcard, state):
    method command (line 204) | def command(self, recipients=False, quietly=False, internal=False):
  class RemoveVCard (line 285) | class RemoveVCard(VCardCommand):
    method command (line 297) | def command(self):
  class VCardAddLines (line 318) | class VCardAddLines(VCardCommand):
    method _get_vcard (line 338) | def _get_vcard(self, handle):
    method command (line 341) | def command(self):
  class VCardSet (line 405) | class VCardSet(VCardAddLines):
    method _get_vcard (line 413) | def _get_vcard(self, handle):
  class VCardRemoveLines (line 424) | class VCardRemoveLines(VCardCommand):
    method command (line 437) | def command(self):
  class ListVCards (line 482) | class ListVCards(VCardCommand):
    method _augment_list_info (line 495) | def _augment_list_info(self, info):
    method command (line 498) | def command(self):
  function ContactVCard (line 549) | def ContactVCard(parent):
  class Contact (line 564) | class Contact(ContactVCard(VCard)):
    method command (line 568) | def command(self, save=True):
  class AddContact (line 607) | class AddContact(ContactVCard(AddVCard)):
  class RemoveContact (line 611) | class RemoveContact(ContactVCard(RemoveVCard)):
  class ListContacts (line 615) | class ListContacts(ContactVCard(ListVCards)):
  class ContactSet (line 620) | class ContactSet(ContactVCard(VCardSet)):
  class ContactImport (line 624) | class ContactImport(Command):
    method command (line 631) | def command(self, format, terms=None, **kwargs):
  class ContactImporters (line 667) | class ContactImporters(Command):
    method command (line 673) | def command(self):
  class AddressSearch (line 687) | class AddressSearch(VCardCommand):
    method _boost_rank (line 698) | def _boost_rank(self, boost, term, *matches):
    method _vcard_addresses (line 709) | def _vcard_addresses(self, cfg, terms, ignored_count, deadline):
    method _index_addresses (line 728) | def _index_addresses(self, cfg, terms, vcard_addresses, count, deadline):
    method command (line 797) | def command(self):
  function ProfileVCard (line 831) | def ProfileVCard(parent):
  class Profile (line 1151) | class Profile(ProfileVCard(VCard)):
  class AddProfile (line 1155) | class AddProfile(ProfileVCard(AddVCard)):
    method _form_defaults (line 1166) | def _form_defaults(self):
    method _before_vcard_create (line 1195) | def _before_vcard_create(self, kind, triplets, vcard=None):
    method _update_vcard_from_post (line 1209) | def _update_vcard_from_post(self, vcard, state=None):
    method _after_vcard_create (line 1241) | def _after_vcard_create(self, kind, vcard, state):
  class EditProfile (line 1245) | class EditProfile(AddProfile):
    method _vcard_to_post_vars (line 1251) | def _vcard_to_post_vars(self, vcard):
    method command (line 1319) | def command(self):
  class RemoveProfile (line 1339) | class RemoveProfile(ProfileVCard(RemoveVCard)):
    method _trash_email (line 1350) | def _trash_email(self, vcard):
    method _delete_keys (line 1359) | def _delete_keys(self, vcard):
    method _cleanup_tags (line 1368) | def _cleanup_tags(self, vcard, delete_tags=False):
    method _unique_usernames (line 1378) | def _unique_usernames(self, vcard):
    method _delete_credentials (line 1396) | def _delete_credentials(self, vcard):
    method _delete_routes (line 1407) | def _delete_routes(self, vcard):
    method _delete_sources (line 1416) | def _delete_sources(self, vcard, delete_tags=False):
    method _trash_email_is_safe (line 1450) | def _trash_email_is_safe(self, vcard):
    method command (line 1458) | def command(self, *args, **kwargs):
  class ListProfiles (line 1490) | class ListProfiles(ProfileVCard(ListVCards)):
  class ProfileSet (line 1495) | class ProfileSet(ProfileVCard(VCardSet)):
  class ChooseFromAddress (line 1499) | class ChooseFromAddress(Command):
    method command (line 1511) | def command(self):
  class ContentTxf (line 1540) | class ContentTxf(EmailTransform):
    method TransformOutgoing (line 1541) | def TransformOutgoing(self, sender, rcpts, msg, **kwargs):

FILE: mailpile/plugins/core.py
  class Load (line 43) | class Load(Command):
    method command (line 50) | def command(self, reset=True, wait=True, wait_all=False, quiet=False):
  class Rescan (line 64) | class Rescan(Command):
    method _progress (line 76) | def _progress(self, progress):
    method command (line 80) | def command(self, slowly=False, cron=False):
    method _rescan_vcards (line 166) | def _rescan_vcards(self, session, which):
    method _run_rescan_command (line 199) | def _run_rescan_command(self, session, timeout=120):
    method _rescan_mailboxes (line 250) | def _rescan_mailboxes(self, session, which='mailboxes', force=True, de...
  class Optimize (line 353) | class Optimize(Command):
    method command (line 358) | def command(self, slowly=False):
  class DeleteMessages (line 370) | class DeleteMessages(Command):
    method command (line 376) | def command(self, slowly=False):
  class BrowseOrLaunch (line 441) | class BrowseOrLaunch(Command):
    method Browse (line 449) | def Browse(cls, sspec):
    method command (line 462) | def command(self):
  class RunWWW (line 481) | class RunWWW(Command):
    method command (line 487) | def command(self):
  class Cleanup (line 523) | class Cleanup(Command):
    method AddTask (line 532) | def AddTask(cls, task, last=False, first=False):
    method command (line 543) | def command(self):
  class WritePID (line 553) | class WritePID(Command):
    method command (line 560) | def command(self):
  class RenderPage (line 568) | class RenderPage(Command):
    method template_path (line 577) | def template_path(self, ttype, template_id=None, **kwargs):
    method command (line 584) | def command(self):
  class ProgramStatus (line 591) | class ProgramStatus(Command):
    class CommandResult (line 599) | class CommandResult(Command.CommandResult):
      method as_text (line 600) | def as_text(self):
    method command (line 657) | def command(self, args=None):
  class CronStatus (line 737) | class CronStatus(Command):
    class CommandResult (line 744) | class CommandResult(Command.CommandResult):
      method as_text (line 745) | def as_text(self):
    method command (line 769) | def command(self, args=None):
  class HealthCheck (line 797) | class HealthCheck(Command):
    method _create_event (line 807) | def _create_event(self):
    method _mem_check (line 826) | def _mem_check(cls, session, config):
    method _disk_check (line 832) | def _disk_check(cls, session, config):
    method _readonly_check (line 838) | def _readonly_check(cls, session, config):
    method check (line 845) | def check(cls, session, config):
    method command (line 885) | def command(self, args=None):
  class GpgCommand (line 890) | class GpgCommand(Command):
    class CommandResult (line 896) | class CommandResult(Command.CommandResult):
      method as_text (line 897) | def as_text(self):
    method command (line 904) | def command(self, args=None):
  class ListDir (line 939) | class ListDir(Command):
    class CommandResult (line 947) | class CommandResult(Command.CommandResult):
      method as_text (line 948) | def as_text(self):
    method command (line 966) | def command(self, args=None):
  class ChangeDir (line 1047) | class ChangeDir(ListDir):
    method command (line 1055) | def command(self, args=None):
  class CatFile (line 1065) | class CatFile(Command):
    class CommandResult (line 1073) | class CommandResult(Command.CommandResult):
      method as_text (line 1074) | def as_text(self):
    method command (line 1080) | def command(self, args=None):
  class ListLanguages (line 1115) | class ListLanguages(Command):
    method command (line 1123) | def command(self):
  class ConfigSet (line 1130) | class ConfigSet(Command):
    method command (line 1147) | def command(self):
  class ConfigAdd (line 1253) | class ConfigAdd(Command):
    method command (line 1266) | def command(self):
  class ConfigUnset (line 1313) | class ConfigUnset(Command):
    method command (line 1324) | def command(self):
  class ConfigPrint (line 1366) | class ConfigPrint(Command):
    method _maybe_all (line 1384) | def _maybe_all(self, list_all, data, key_types, recurse, sanitize):
    method command (line 1411) | def command(self):
  class ConfigureMailboxes (line 1464) | class ConfigureMailboxes(Command):
    method _truthy (line 1492) | def _truthy(self, var, default='n'):
    method command (line 1495) | def command(self):
  class Output (line 1672) | class Output(Command):
    method etag_data (line 1682) | def etag_data(self):
    method max_age (line 1685) | def max_age(self):
    method get_render_mode (line 1688) | def get_render_mode(self):
    method command (line 1691) | def command(self):
  class Pipe (line 1697) | class Pipe(Command):
    class CommandResult (line 1706) | class CommandResult(Command.CommandResult):
      method as_text (line 1707) | def as_text(self):
    method command (line 1719) | def command(self):
  class Quit (line 1793) | class Quit(Command):
    method command (line 1806) | def command(self):
  class IdleQuit (line 1823) | class IdleQuit(Command):
    method check (line 1829) | def check(self):
    method command (line 1834) | def command(self):
  class TrustingQQQ (line 1844) | class TrustingQQQ(Command):
    method command (line 1849) | def command(self):
  class Abort (line 1858) | class Abort(Command):
    method command (line 1869) | def command(self):
  class Help (line 1880) | class Help(Command):
    class CommandResult (line 1888) | class CommandResult(Command.CommandResult):
      method splash_as_text (line 1890) | def splash_as_text(self):
      method variables_as_text (line 1935) | def variables_as_text(self):
      method commands_as_text (line 1949) | def commands_as_text(self):
      method as_text (line 1991) | def as_text(self):
    method command (line 2001) | def command(self):
    method _starting (line 2056) | def _starting(self):
    method _finishing (line 2059) | def _finishing(self, rv, *args, **kwargs):
  class HelpVars (line 2065) | class HelpVars(Help):
    method command (line 2073) | def command(self):
  class HelpSplash (line 2104) | class HelpSplash(Help):
    method command (line 2110) | def command(self, interactive=True):

FILE: mailpile/plugins/crypto_autocrypt.py
  function save_Autocrypt_DB (line 88) | def save_Autocrypt_DB(config):
  function get_Autocrypt_DB (line 93) | def get_Autocrypt_DB(config):
  class AutocryptRecord (line 103) | class AutocryptRecord(object):
    method __init__ (line 111) | def __init__(self, to,
    method float_ratio (line 130) | def float_ratio(self):
    method update_ratio (line 133) | def update_ratio(self, have_key=True):
    method should_encrypt (line 141) | def should_encrypt(self):
    method as_list (line 147) | def as_list(self):
    method as_dict (line 150) | def as_dict(self):
    method as_text (line 155) | def as_text(self):
    method save_to (line 158) | def save_to(self, db):
    method Load (line 163) | def Load(cls, db, to, _raise=KeyError):
  function autocrypt_process_email (line 173) | def autocrypt_process_email(config, msg, msg_mid, msg_ts, sender_email,
  function autocrypt_recommendation (line 278) | def autocrypt_recommendation(config, email, re_encrypted=False, state_db...
  function autocrypt_policy_checker (line 324) | def autocrypt_policy_checker(session, profile, emails):
  class AutocryptSearch (line 345) | class AutocryptSearch(Command):
    class CommandResult (line 352) | class CommandResult(Command.CommandResult):
      method as_text (line 353) | def as_text(self):
    method command (line 364) | def command(self):
  class AutocryptForget (line 380) | class AutocryptForget(Command):
    method command (line 387) | def command(self):
  class AutocryptParse (line 406) | class AutocryptParse(Command):
    method command (line 412) | def command(self):
  class AutocryptPeers (line 432) | class AutocryptPeers(Command):
    class CommandResult (line 438) | class CommandResult(Command.CommandResult):
      method as_text (line 439) | def as_text(self):
    method command (line 446) | def command(self):
  function autocrypt_meta_kwe (line 455) | def autocrypt_meta_kwe(index, msg_mid, msg, msg_size, msg_ts,
  class AutocryptTxf (line 497) | class AutocryptTxf(EmailTransform):
    method TransformOutgoing (line 504) | def TransformOutgoing(self, sender, rcpts, msg, **kwargs):
  class AutocryptKeyLookupHandler (line 557) | class AutocryptKeyLookupHandler(EmailKeyLookupHandler):
    method __init__ (line 565) | def __init__(self, session, *args, **kwargs):
    method _score (line 568) | def _score(self, key):
    method _db_and_acr (line 571) | def _db_and_acr(self, address):
    method _getkey (line 578) | def _getkey(self, email, keyinfo):
    method _lookup (line 590) | def _lookup(self, address, strict_email_match=False):

FILE: mailpile/plugins/crypto_gnupg.py
  class ContentTxf (line 35) | class ContentTxf(EmailTransform):
    method _wrap_key_in_html (line 36) | def _wrap_key_in_html(self, title, keydata):
    method _wrap_key_in_html_vars (line 44) | def _wrap_key_in_html_vars(self, title, keydata):
    method TransformOutgoing (line 56) | def TransformOutgoing(self, sender, rcpts, msg, **kwargs):
  class CryptoTxf (line 147) | class CryptoTxf(EmailTransform):
    method TransformOutgoing (line 148) | def TransformOutgoing(self, sender, rcpts, msg,
  class GPGKeySearch (line 201) | class GPGKeySearch(Command):
    class CommandResult (line 208) | class CommandResult(Command.CommandResult):
      method as_text (line 209) | def as_text(self):
    method command (line 215) | def command(self):
  class GPGKeyReceive (line 223) | class GPGKeyReceive(Command):
    method command (line 231) | def command(self):
  class GPGKeyImport (line 244) | class GPGKeyImport(Command):
    method command (line 258) | def command(self):
  class GPGKeySign (line 288) | class GPGKeySign(Command):
    method command (line 297) | def command(self):
  class GPGKeyImportFromMail (line 318) | class GPGKeyImportFromMail(Search):
    class CommandResult (line 328) | class CommandResult(Command.CommandResult):
      method __init__ (line 329) | def __init__(self, *args, **kwargs):
      method as_text (line 332) | def as_text(self):
    method command (line 340) | def command(self):
  class GPGKeyList (line 368) | class GPGKeyList(Command):
    method command (line 376) | def command(self):
  class GPGKeyExport (line 390) | class GPGKeyExport(Command):
    class CommandResult (line 400) | class CommandResult(Command.CommandResult):
      method __init__ (line 401) | def __init__(self, *args, **kwargs):
      method as_text (line 404) | def as_text(self):
    method command (line 409) | def command(self):
  class GPGKeyListSecret (line 437) | class GPGKeyListSecret(Command):
    method command (line 444) | def command(self):
  class GPGUsageStatistics (line 456) | class GPGUsageStatistics(Search):
    class CommandResult (line 465) | class CommandResult(Command.CommandResult):
      method __init__ (line 466) | def __init__(self, *args, **kwargs):
      method as_text (line 469) | def as_text(self):
    method command (line 478) | def command(self):
  class GPGCheckKeys (line 511) | class GPGCheckKeys(Search):
    class CommandResult (line 521) | class CommandResult(Command.CommandResult):
      method __init__ (line 522) | def __init__(self, *args, **kwargs):
      method as_text (line 525) | def as_text(self):
    method _fix_gen_key (line 541) | def _fix_gen_key(self, min_bits=2048):
    method _fix_mp_config (line 548) | def _fix_mp_config(self, good_key=None):
    method _fix_revoke_key (line 557) | def _fix_revoke_key(self, fprint, comment=''):
    method _fix_disable_key (line 567) | def _fix_disable_key(self, fprint, comment=''):
    method command (line 574) | def command(self):

FILE: mailpile/plugins/crypto_policy.py
  function register_crypto_policy (line 26) | def register_crypto_policy(name, checker):
  class CryptoPolicyBaseAction (line 34) | class CryptoPolicyBaseAction(Command):
  class UpdateCryptoPolicyForUser (line 39) | class UpdateCryptoPolicyForUser(CryptoPolicyBaseAction):
    method command (line 48) | def command(self):
    method _parse_args (line 66) | def _parse_args(self):
  class CryptoPolicy (line 80) | class CryptoPolicy(CryptoPolicyBaseAction):
    method ShouldAttachKey (line 90) | def ShouldAttachKey(cls, config, vcards=None, emails=None, ttl=90):
    method _vcard_policy (line 135) | def _vcard_policy(self, config, email):
    method _encryption_ratio (line 144) | def _encryption_ratio(self, session, idx, email, minimum=5):
    method crypto_policy (line 160) | def crypto_policy(cls, session, idx, emails, should_encrypt=False):
    method command (line 277) | def command(self):

FILE: mailpile/plugins/cryptostate.py
  function text_kw_extractor (line 13) | def text_kw_extractor(index, msg, ctype, text, **kwargs):
  function meta_kw_extractor (line 21) | def meta_kw_extractor(index, msg_mid, msg, msg_size, msg_ts, **kwargs):
  function search (line 93) | def search(config, idx, term, hits):

FILE: mailpile/plugins/dates.py
  function meta_kw_extractor (line 14) | def meta_kw_extractor(index, msg_mid, msg, msg_size, msg_ts, **kwargs):
  function _adjust (line 30) | def _adjust(d):
  function _mk_date (line 39) | def _mk_date(ts):
  function search (line 54) | def search(config, idx, term, hits):

FILE: mailpile/plugins/eventlog.py
  class Events (line 15) | class Events(Command):
    method command (line 44) | def command(self):
  class Cancel (line 131) | class Cancel(Command):
    method command (line 142) | def command(self):
  class Undo (line 162) | class Undo(Command):
    method command (line 172) | def command(self):
  class Watch (line 194) | class Watch(Command):
    method command (line 201) | def command(self):

FILE: mailpile/plugins/exporters.py
  class ExportMail (line 29) | class ExportMail(Command):
    method export_path (line 35) | def export_path(self, mbox_type):
    method create_mailbox (line 41) | def create_mailbox(self, mbox_type, path):
    method command (line 49) | def command(self, save=True):

FILE: mailpile/plugins/groups.py
  function search (line 14) | def search(config, idx, term, hits):
  function GroupVCard (line 35) | def GroupVCard(parent):
  class Group (line 69) | class Group(GroupVCard(VCard)):
  class AddGroup (line 73) | class AddGroup(GroupVCard(AddVCard)):
  class GroupAddLines (line 77) | class GroupAddLines(GroupVCard(VCardAddLines)):
  class RemoveGroup (line 81) | class RemoveGroup(GroupVCard(RemoveVCard)):
  class ListGroups (line 85) | class ListGroups(GroupVCard(ListVCards)):

FILE: mailpile/plugins/gui.py
  function UpdateGUIState (line 23) | def UpdateGUIState():
  class GuiOMaticConnection (line 28) | class GuiOMaticConnection(threading.Thread):
    method __init__ (line 29) | def __init__(self, config, sock, main=False):
    method _do (line 39) | def _do(self, command, **args):
    method _select_sleep (line 49) | def _select_sleep(self, seconds):
    method _state_startup (line 52) | def _state_startup(self, in_state):
    method _state_need_setup (line 74) | def _state_need_setup(self, in_state):
    method _state_please_log_in (line 90) | def _state_please_log_in(self, in_state):
    method _state_loading_index (line 104) | def _state_loading_index(self, in_state):
    method _state_logged_in (line 113) | def _state_logged_in(self, in_state):
    method _state_shutting_down (line 130) | def _state_shutting_down(self, in_state):
    method _choose_state (line 136) | def _choose_state(self):
    method change_state (line 149) | def change_state(self):
    method new_mail_notifications (line 177) | def new_mail_notifications(self, summarize=False):
    method run (line 246) | def run(self):
  class ConnectToGuiOMatic (line 266) | class ConnectToGuiOMatic(Command):
    method command (line 275) | def command(self):

FILE: mailpile/plugins/html_magic.py
  class JsApi (line 26) | class JsApi(RenderPage):
    method max_age (line 34) | def max_age(self):
    method etag_data (line 42) | def etag_data(self):
    method command (line 53) | def command(self, save=True, auto=False):
  class ProgressiveWebApp (line 115) | class ProgressiveWebApp(RenderPage):
    method command (line 123) | def command(self):
  class HttpProxyGetRequest (line 127) | class HttpProxyGetRequest(Command):
    method command (line 141) | def command(self):

FILE: mailpile/plugins/keylookup/__init__.py
  function register_crypto_key_lookup_handler (line 44) | def register_crypto_key_lookup_handler(handler):
  function _score_validity (line 51) | def _score_validity(validity, local=False):
  function _update_scores (line 66) | def _update_scores(session, key_id, key_info, known_keys_list):
  function _normalize_key (line 127) | def _normalize_key(session, key_info):
  function _mailpile_key_list (line 152) | def _mailpile_key_list(gpgi_key_list):
  function lookup_crypto_keys (line 160) | def lookup_crypto_keys(session, address,
  class KeyLookup (line 289) | class KeyLookup(Command):
    method command (line 301) | def command(self):
  class KeyImport (line 336) | class KeyImport(Command):
    method _get_or_create_vcard (line 349) | def _get_or_create_vcard(self, address):
    method command (line 359) | def command(self):
  class KeyTofu (line 402) | class KeyTofu(Command):
    method _key_can_encrypt (line 411) | def _key_can_encrypt(self, gnupg, fingerprint):
    method _recently_used_crypto (line 415) | def _recently_used_crypto(self, tofu_cfg, idx, email):
    method _key_is_trusted (line 439) | def _key_is_trusted(self, fingerprint, known_keys_list):
    method _seen_enough_signatures (line 445) | def _seen_enough_signatures(self, tofu_cfg, idx, email, keyinfo):
    method command (line 455) | def command(self):
  class LookupHandler (line 615) | class LookupHandler:
    method __init__ (line 624) | def __init__(self, session, known_keys_list):
    method _gnupg (line 628) | def _gnupg(self):
    method _score (line 631) | def _score(self, key):
    method _getkey (line 634) | def _getkey(self, email, key):
    method _gk_succeeded (line 637) | def _gk_succeeded(self, result):
    method _lookup (line 641) | def _lookup(self, address, strict_email_match=False):
    method lookup (line 644) | def lookup(self, address, strict_email_match=False, get=None):
    method key_import (line 677) | def key_import(self, address):
  class KeychainLookupHandler (line 681) | class KeychainLookupHandler(LookupHandler):
    method _score (line 689) | def _score(self, key):
    method _lookup (line 692) | def _lookup(self, address, strict_email_match):
    method _getkey (line 713) | def _getkey(self, email, key):
  class KeyserverLookupHandler (line 725) | class KeyserverLookupHandler(LookupHandler):
    method _score (line 744) | def _score(self, key):
    method _lookup_url (line 747) | def _lookup_url(self, url_base, address):
    method _lookup (line 754) | def _lookup(self, address, strict_email_match=False):
    method _getkey_url (line 806) | def _getkey_url(self, url_base, email, key):
    method _getkey (line 814) | def _getkey(self, email, key):
  class VerifyingKeyserverLookupHandler (line 849) | class VerifyingKeyserverLookupHandler(KeyserverLookupHandler):
    method _lookup_url (line 862) | def _lookup_url(self, url_base, address):
    method _score (line 867) | def _score(self, key):

FILE: mailpile/plugins/keylookup/email_keylookup.py
  function _PRUNE_GLOBAL_KEY_CACHE (line 22) | def _PRUNE_GLOBAL_KEY_CACHE():
  function _might_be_pgp_key (line 30) | def _might_be_pgp_key(filename, mimetype):
  class EmailKeyLookupHandler (line 38) | class EmailKeyLookupHandler(LookupHandler, Search):
    method __init__ (line 47) | def __init__(self, session, *args, **kwargs):
    method _score (line 55) | def _score(self, key):
    method _lookup (line 58) | def _lookup(self, address, strict_email_match=False):
    method _getkey (line 79) | def _getkey(self, email, keyinfo):
    method _get_message_keys (line 86) | def _get_message_keys(self, messageid,
  function get_pgp_key_keywords (line 128) | def get_pgp_key_keywords(data):
  function has_pgpkey_data_kw_extractor (line 144) | def has_pgpkey_data_kw_extractor(index, msg, mimetype, filename, part, l...

FILE: mailpile/plugins/keylookup/wkd.py
  function _zbase_encode (line 30) | def _zbase_encode(data):
  function WebKeyDirectoryURLs (line 53) | def WebKeyDirectoryURLs(address, plusmagic=True):
  class WKDLookupHandler (line 72) | class WKDLookupHandler(LookupHandler):
    method __init__ (line 119) | def __init__(self, *args, **kwargs):
    method _score (line 123) | def _score(self, key):
    method _lookup (line 126) | def _lookup(self, address, strict_email_match=True):
    method _getkey (line 200) | def _getkey(self, email, keyinfo):
  class GetWebKeyDirectoryURLs (line 210) | class GetWebKeyDirectoryURLs(Command):
    method command (line 214) | def command(self):

FILE: mailpile/plugins/migrate.py
  function migrate_routes (line 18) | def migrate_routes(session):
  function migrate_mailboxes (line 60) | def migrate_mailboxes(session):
  function migrate_cleanup (line 153) | def migrate_cleanup(session):
  class Migrate (line 201) | class Migrate(Command):
    method command (line 208) | def command(self, before_setup=True, after_setup=True):

FILE: mailpile/plugins/motd.py
  class MessageOfTheDay (line 48) | class MessageOfTheDay(Command):
    method _disable_updates (line 56) | def _disable_updates(cls, session):
    method update (line 65) | def update(cls, session):
    method _get (line 69) | def _get(self, url):
    class CommandResult (line 84) | class CommandResult(Command.CommandResult):
      method as_text (line 85) | def as_text(self):
    method command (line 98) | def command(self):

FILE: mailpile/plugins/oauth.py
  class OAuth2 (line 44) | class OAuth2(TestableWebbable):
    method RedirectURI (line 78) | def RedirectURI(cls, config, oauth2_cfg, http_host=None):
    method ActivateHardCodedOAuth (line 94) | def ActivateHardCodedOAuth(cls, config):
    method GetOAuthConfig (line 100) | def GetOAuthConfig(cls, config, hostname=None, oname=None):
    method GetOAuthURLVars (line 110) | def GetOAuthURLVars(cls, session, ocfg, username):
    method GetOAuthURL (line 123) | def GetOAuthURL(cls, session, ocfg, username, url_vars=None):
    method XOAuth2Response (line 129) | def XOAuth2Response(cls, username, token_info):
    method GetToken (line 134) | def GetToken(cls, session, oauth2_cfg, code, tok_id=None):
    method GetFreshTokenInfo (line 172) | def GetFreshTokenInfo(cls, session, tok_id):
    method setup_command (line 202) | def setup_command(self, session):

FILE: mailpile/plugins/plugins.py
  class Plugins (line 13) | class Plugins(mailpile.commands.Command):
    method command (line 20) | def command(self):
  class LoadPlugin (line 36) | class LoadPlugin(mailpile.commands.Command):
    method command (line 46) | def command(self):
  class DisablePlugin (line 78) | class DisablePlugin(mailpile.commands.Command):
    method command (line 88) | def command(self):

FILE: mailpile/plugins/search.py
  class SearchResults (line 30) | class SearchResults(dict):
    method _name (line 34) | def _name(self, sender, short=True, full_email=False):
    method _names (line 49) | def _names(self, senders):
    method _compact (line 64) | def _compact(self, namelist, maxlen):
    method _metadata (line 87) | def _metadata(self, msg_info):
    method _msg_addresses (line 182) | def _msg_addresses(self, msg_info=None, addresses=[],
    method _address (line 205) | def _address(self, cid=None, e=None, n=None):
    method _msg_tags (line 213) | def _msg_tags(self, msg_info):
    method _tag (line 218) | def _tag(self, tid, attributes={}):
    method _thread (line 229) | def _thread(self, thread_mid):
    method _prune_msg_tree (line 310) | def _prune_msg_tree(self, tree):
    method _troubleshoot_missing_message (line 319) | def _troubleshoot_missing_message(self, email, tree):
    method _message (line 356) | def _message(self, email):
    method __init__ (line 406) | def __init__(self, session, idx,
    method add_msg_info (line 514) | def add_msg_info(self, mid, msg_info, full_threads=False, idxs=None):
    method add_email (line 541) | def add_email(self, e, idxs=None):
    method __nonzero__ (line 554) | def __nonzero__(self):
    method next_set (line 557) | def next_set(self):
    method previous_set (line 563) | def previous_set(self):
    method _fix_width (line 569) | def _fix_width(self, text, width):
    method as_text (line 582) | def as_text(self):
  class Search (line 686) | class Search(Command):
    class CommandResult (line 707) | class CommandResult(Command.CommandResult):
      method __init__ (line 708) | def __init__(self, *args, **kwargs):
      method _fixup (line 717) | def _fixup(self):
      method as_text (line 723) | def as_text(self):
      method as_html (line 735) | def as_html(self, *args, **kwargs):
      method as_dict (line 739) | def as_dict(self, *args, **kwargs):
    method __init__ (line 743) | def __init__(self, *args, **kwargs):
    method _idx (line 749) | def _idx(self, **kwargs):
    method state_as_query_args (line 752) | def state_as_query_args(self):
    method _starting (line 758) | def _starting(self):
    method _email_view_side_effects (line 836) | def _email_view_side_effects(self, emails):
    method switch_indexes (line 849) | def switch_indexes(self, path):
    method _do_search (line 860) | def _do_search(self, search=None, process_args=False):
    method cache_id (line 986) | def cache_id(self, *args, **kwargs):
    method cache_requirements (line 991) | def cache_requirements(self, result):
    method command (line 1011) | def command(self):
  class Next (line 1029) | class Next(Search):
    method command (line 1036) | def command(self):
  class Previous (line 1048) | class Previous(Search):
    method command (line 1055) | def command(self):
  class Order (line 1067) | class Order(Search):
    method command (line 1074) | def command(self):
  class View (line 1083) | class View(Search):
    class RawResult (line 1092) | class RawResult(dict):
      method _decode (line 1093) | def _decode(self):
      method as_text (line 1102) | def as_text(self, *args, **kwargs):
      method as_html (line 1105) | def as_html(self, *args, **kwargs):
    method _side_effects (line 1108) | def _side_effects(self, emails):
    method state_as_query_args (line 1112) | def state_as_query_args(self):
    method command (line 1115) | def command(self):
  class Extract (line 1172) | class Extract(Command):
    class CommandResult (line 1179) | class CommandResult(Command.CommandResult):
      method __init__ (line 1180) | def __init__(self, *args, **kwargs):
      method _fixup (line 1184) | def _fixup(self):
      method as_html (line 1194) | def as_html(self, *args, **kwargs):
      method as_dict (line 1198) | def as_dict(self, *args, **kwargs):
    method command (line 1202) | def command(self):
  function mailbox_search (line 1252) | def mailbox_search(config, idx, term, hits):

FILE: mailpile/plugins/setup_magic.py
  class SetupMagic (line 44) | class SetupMagic(Command):
    method basic_app_config (line 202) | def basic_app_config(self, session,
    method make_master_key (line 327) | def make_master_key(self):
    method URLGet (line 367) | def URLGet(cls, session, url, data=None):
    method _urlget (line 376) | def _urlget(self, url, data=None):
    method setup_command (line 379) | def setup_command(self, session):
    method command (line 382) | def command(self, *args, **kwargs):
  class TestableWebbable (line 386) | class TestableWebbable(SetupMagic):
    method _advance (line 397) | def _advance(self):
    method _success (line 415) | def _success(self, message, result=True, advance=False):
    method _testing (line 420) | def _testing(self):
    method _testing_yes (line 424) | def _testing_yes(self, method, *args, **kwargs):
    method _testing_data (line 433) | def _testing_data(self, method, tdata, *args, **kwargs):
    method setup_command (line 439) | def setup_command(self, session):
  class SetupGetEmailSettings (line 443) | class SetupGetEmailSettings(TestableWebbable):
    method _progress (line 469) | def _progress(self, message):
    method _log_result (line 481) | def _log_result(self, message):
    method _username (line 488) | def _username(self, val, email):
    method _guess_username (line 493) | def _guess_username(self, email):
    method _source_proto (line 501) | def _source_proto(self, insrv):
    method _route_proto (line 512) | def _route_proto(self, outsrv):
    method _rank (line 523) | def _rank(self, entry):
    method _clean_domain (line 541) | def _clean_domain(self, domain):
    method _get_xml_autoconfig (line 554) | def _get_xml_autoconfig(self, url, domain, email):
    method _get_ispdb (line 608) | def _get_ispdb(self, email, domain):
    method _want_anonymity (line 629) | def _want_anonymity(self):
    method _get_mx1 (line 633) | def _get_mx1(self, domain):
    method _get_domain_autoconfig (line 653) | def _get_domain_autoconfig(self, email, domain, mx1, ssl=True):
    method _guess_service_domains (line 671) | def _guess_service_domains(self, domain,
    method _probe_port (line 717) | def _probe_port(self, host, port, encrypted=False):
    method _guess_settings (line 732) | def _guess_settings(self, email, domain, mx1):
    method _get_email_settings (line 778) | def _get_email_settings(self, email):
    method _test_login_and_proto (line 824) | def _test_login_and_proto(self, email, settings):
    method _probe_account_settings (line 858) | def _probe_account_settings(self, email, results):
    method setup_command (line 954) | def setup_command(self, session):
  class SetupWelcome (line 991) | class SetupWelcome(TestableWebbable):
    method bg_setup_stage_1 (line 998) | def bg_setup_stage_1(self):
    method configure_language (line 1008) | def configure_language(self, session, config, language, save=True):
    method setup_command (line 1020) | def setup_command(self, session):
  class CreatePassword (line 1041) | class CreatePassword(TestableWebbable):
    method find_dictionaries (line 1049) | def find_dictionaries(self):
    method load_dictionary (line 1062) | def load_dictionary(self, dpath, maxlen=6):
    method setup_command (line 1066) | def setup_command(self, session):
  class SetupPassword (line 1103) | class SetupPassword(TestableWebbable):
    method setup_command (line 1114) | def setup_command(self, session):
  class SetupTestRoute (line 1149) | class SetupTestRoute(TestableWebbable):
    method setup_command (line 1160) | def setup_command(self, session):
  class SetupTor (line 1216) | class SetupTor(TestableWebbable):
    method autoconfig (line 1224) | def autoconfig(cls, session):
    method auto_configure_tor (line 1227) | def auto_configure_tor(self, session):
    method _configure_tor (line 1254) | def _configure_tor(self, session, hostport, port_zero=False):
    method setup_command (line 1289) | def setup_command(self, session):
  class Setup (line 1302) | class Setup(SetupWelcome):
    method _CHECKPOINTS (line 1318) | def _CHECKPOINTS(self, config):
    method Next (line 1328) | def Next(cls, config, default, needed_auth=True):
    method cli_setup_command (line 1342) | def cli_setup_command(self, session):
    method setup_command (line 1384) | def setup_command(self, session):

FILE: mailpile/plugins/sizes.py
  function meta_kw_extractor (line 15) | def meta_kw_extractor(index, msg_mid, msg, msg_size, msg_ts, **kwargs):
  function _mk_logsize (line 40) | def _mk_logsize(size, default_unit=0):
  function search (line 59) | def search(config, idx, term, hits):

FILE: mailpile/plugins/smtp_server.py
  class SMTPChannel (line 31) | class SMTPChannel(smtpd.SMTPChannel):
    method __init__ (line 36) | def __init__(self, session, *args, **kwargs):
    method _is_dangerous_address (line 45) | def _is_dangerous_address(self, address):
    method _is_spam_address (line 48) | def _is_spam_address(self, address):
    method push (line 51) | def push(self, msg):
    method _address_ok (line 63) | def _address_ok(self, address):
    method _challenge (line 70) | def _challenge(self):
    method _hashgrey_ok (line 75) | def _hashgrey_ok(self, address):
    method smtp_MAIL (line 100) | def smtp_MAIL(self, arg):
    method smtp_RCPT (line 112) | def smtp_RCPT(self, arg):
    method smtp_DATA (line 129) | def smtp_DATA(self, arg):
    method collect_incoming_data (line 136) | def collect_incoming_data(self, data):
  class SMTPServer (line 145) | class SMTPServer(smtpd.SMTPServer):
    method __init__ (line 146) | def __init__(self, session, localaddr, **kwargs):
    method handle_accept (line 150) | def handle_accept(self):
    method process_message (line 156) | def process_message(self, peer, mailfrom, rcpttos, data):
  class SMTPWorker (line 179) | class SMTPWorker(threading.Thread):
    method __init__ (line 180) | def __init__(self, session):
    method run (line 185) | def run(self):
    method quit (line 193) | def quit(self, join=True):
  class HashCash (line 199) | class HashCash(Command):
    method command (line 206) | def command(self):

FILE: mailpile/plugins/tags.py
  function GetFilters (line 76) | def GetFilters(cfg, filter_on=None, types=FILTER_TYPES[:1]):
  function FilterMove (line 95) | def FilterMove(cfg, filter_id, filter_new_id):
  function FilterDelete (line 110) | def FilterDelete(cfg, *filter_ids):
  function GetTags (line 122) | def GetTags(cfg, tn=None, default=None, **kwargs):
  function GetTag (line 172) | def GetTag(cfg, tn, default=None):
  function GetTagID (line 176) | def GetTagID(cfg, tn):
  function GuessTags (line 181) | def GuessTags(cfg, name):
  function Slugify (line 208) | def Slugify(tag_name, tags=None):
  function GetTagInfo (line 219) | def GetTagInfo(cfg, tn, stats=False, unread=None, exclude=None, subtags=...
  class TagCommand (line 268) | class TagCommand(Command):
    method _reorder_all_tags (line 269) | def _reorder_all_tags(self):
    method finish (line 278) | def finish(self, save=True):
  class Tag (line 284) | class Tag(TagCommand):
    class CommandResult (line 300) | class CommandResult(TagCommand.CommandResult):
      method as_text (line 301) | def as_text(self):
    method _get_ops_and_msgids (line 322) | def _get_ops_and_msgids(self, words):
    method _do_tagging (line 363) | def _do_tagging(self, ops, msg_ids, conversations,
    method Undo (line 455) | def Undo(cls, undo, event):
    method command (line 474) | def command(self, **kwargs):
  class TagLater (line 480) | class TagLater(Tag):
    method command (line 484) | def command(self, **kwargs):
  class TagTemporarily (line 497) | class TagTemporarily(Tag):
    method command (line 501) | def command(self, **kwargs):
  class AddTag (line 509) | class AddTag(TagCommand):
    class CommandResult (line 539) | class CommandResult(TagCommand.CommandResult):
      method as_text (line 540) | def as_text(self):
    method command (line 548) | def command(self, save=True):
  class ListTags (line 605) | class ListTags(TagCommand):
    method cache_requirements (line 613) | def cache_requirements(self, result):
    class CommandResult (line 620) | class CommandResult(TagCommand.CommandResult):
      method as_text (line 621) | def as_text(self):
    method command (line 637) | def command(self, **kwargs):
  class DeleteTag (line 727) | class DeleteTag(TagCommand):
    class CommandResult (line 737) | class CommandResult(TagCommand.CommandResult):
      method as_text (line 738) | def as_text(self):
    method command (line 746) | def command(self):
  class TagAutomation (line 785) | class TagAutomation(Command):
    method _tags (line 792) | def _tags(self, args):
    method _perform_auto_action (line 801) | def _perform_auto_action(self, session, tag, dry_run=False):
    method command (line 818) | def command(self):
    method run_in_background (line 855) | def run_in_background(cls, session):
  class FilterCommand (line 870) | class FilterCommand(Command):
    method finish (line 871) | def finish(self, save=True):
  class Filter (line 876) | class Filter(FilterCommand):
    method _truthy (line 898) | def _truthy(self, var):
    method command (line 901) | def command(self, save=True):
  class DeleteFilter (line 1018) | class DeleteFilter(FilterCommand):
    method command (line 1025) | def command(self):
  class ListFilters (line 1047) | class ListFilters(Command):
    class CommandResult (line 1058) | class CommandResult(Command.CommandResult):
      method as_text (line 1059) | def as_text(self):
    method command (line 1067) | def command(self, want_fid=None):
  class MoveFilter (line 1113) | class MoveFilter(ListFilters):
    method command (line 1120) | def command(self):

FILE: mailpile/plugins/vcard_carddav.py
  class DAVClient (line 19) | class DAVClient:
    method __init__ (line 20) | def __init__(self, host,
    method request (line 43) | def request(self, url, method, headers={}, body=""):
    method options (line 75) | def options(self, url):
  class CardDAV (line 80) | class CardDAV(DAVClient):
    method __init__ (line 81) | def __init__(self, host, url,
    method cd (line 89) | def cd(self, url):
    method _check_capability (line 92) | def _check_capability(self):
    method get_vcard (line 96) | def get_vcard(self, url):
    method put_vcard (line 102) | def put_vcard(self, url, vcard):
    method list_vcards (line 105) | def list_vcards(self):
  class CardDAVImporter (line 114) | class CardDAVImporter(VCardImporter):
    method get_contacts (line 129) | def get_contacts(self):
    method filter_contacts (line 138) | def filter_contacts(self, terms):

FILE: mailpile/plugins/vcard_gnupg.py
  class GnuPGImporter (line 21) | class GnuPGImporter(VCardImporter):
    method get_guid (line 37) | def get_guid(self, vcard):
    method import_vcards (line 40) | def import_vcards(self, session, vcard_store, *args, **kwargs):
    method get_vcards (line 45) | def get_vcards(self,
    method key_is_useless (line 86) | def key_is_useless(cls, key):
    method key_vcl (line 92) | def key_vcl(cls, key_id, key):
    method vcards_one_per_uid (line 103) | def vcards_one_per_uid(cls, keys, vcards, kindhint=None):
    method vcards_per_key (line 129) | def vcards_per_key(cls, keys, vcards):
    method vcards_merged (line 156) | def vcards_merged(cls, keys, vcards):
    method gnupg_keys_as_vcards (line 186) | def gnupg_keys_as_vcards(cls, gnupg,
  class PGPKeysAsVCards (line 217) | class PGPKeysAsVCards(Command):
    class CommandResult (line 228) | class CommandResult(Command.CommandResult):
      method as_text (line 229) | def as_text(self):
    method command (line 236) | def command(self):
  class PGPKeysImportAsVCards (line 255) | class PGPKeysImportAsVCards(Command):
    method command (line 268) | def command(self):

FILE: mailpile/plugins/vcard_gravatar.py
  class GravatarImporter (line 18) | class GravatarImporter(VCardImporter):
    method _want_update (line 46) | def _want_update(self):
    method _get (line 68) | def _get(self, url):
    method check_gravatar (line 74) | def check_gravatar(self, vcard, email):
    method get_vcards (line 113) | def get_vcards(self):

FILE: mailpile/plugins/vcard_libravatar.py
  class LibravatarImporter (line 15) | class LibravatarImporter(VCardImporter):
    method _want_update (line 44) | def _want_update(self):
    method _get (line 67) | def _get(self, url):
    method check_libravatar (line 73) | def check_libravatar(self, vcard, email):
    method get_vcards (line 112) | def get_vcards(self):

FILE: mailpile/plugins/vcard_mork.py
  function hexcmp (line 18) | def hexcmp(x, y):
  class MorkImporter (line 32) | class MorkImporter(VCardImporter):
    class Database (line 47) | class Database:
      method __init__ (line 48) | def __init__(self):
    class Table (line 53) | class Table:
      method __init__ (line 54) | def __init__(self):
    class Row (line 60) | class Row:
      method __init__ (line 61) | def __init__(self):
    class Cell (line 66) | class Cell:
      method __init__ (line 67) | def __init__(self):
    method escapeData (line 71) | def escapeData(self, match):
    method escapeMindy (line 84) | def escapeMindy(self, match):
    method encodeMindyValue (line 96) | def encodeMindyValue(self, value):
    method unescapeMork (line 110) | def unescapeMork(self, match):
    method decodeMorkValue (line 117) | def decodeMorkValue(self, value):
    method addToDict (line 122) | def addToDict(self, dict, cells):
    method getRowIdScope (line 129) | def getRowIdScope(self, rowid, cdict):
    method delRow (line 136) | def delRow(self, db, table, rowid):
    method addRow (line 146) | def addRow(self, db, table, rowid, cells):
    method inputMork (line 179) | def inputMork(self, data):
    method morkToHash (line 310) | def morkToHash(self):
    method load (line 334) | def load(self):
    method get_vcards (line 343) | def get_vcards(self):

FILE: mailpile/plugins/webterminal.py
  class TerminalSessionNew (line 17) | class TerminalSessionNew(Command):
    method command (line 26) | def command(self):
  class TerminalSessionEnd (line 39) | class TerminalSessionEnd(Command):
    method command (line 51) | def command(self):
  class TerminalCommand (line 64) | class TerminalCommand(Command):
    method command (line 79) | def command(self):

FILE: mailpile/postinglist.py
  function PLC_CACHE_FlushAndClean (line 40) | def PLC_CACHE_FlushAndClean(session, min_changes=0, keep=5, runtime=None):
  class PostingListContainer (line 78) | class PostingListContainer(object):
    method Load (line 85) | def Load(cls, session, sig, uncached_cb=None):
    method __init__ (line 104) | def __init__(self, session, sig, fd=None):
    method get (line 116) | def get(self, sig, default=None):
    method add (line 119) | def add(self, *args, **kwargs):
    method remove (line 123) | def remove(self, *args, **kwargs):
    method purge_deleted (line 128) | def purge_deleted(self, deleted_sig, deleted_set):
    method save (line 138) | def save(self, split=True):
    method _splits (line 185) | def _splits(self):
    method _load (line 222) | def _load(self):
    method _unlocked_parse_lines (line 245) | def _unlocked_parse_lines(self, lines):
    method _unlocked_add (line 251) | def _unlocked_add(self, sig, values):
    method _unlocked_remove (line 259) | def _unlocked_remove(self, sig, values):
    method _SaveFile (line 268) | def _SaveFile(cls, config, sig):
    method _GetFilenameAndSig (line 272) | def _GetFilenameAndSig(cls, config, sig):
  class PostingList (line 292) | class PostingList(object):
    method Append (line 298) | def Append(cls, session, word, values, compact=False, sig=None):
    method Optimize (line 303) | def Optimize(cls, session, index, lazy=False, quick=False):
    method __init__ (line 307) | def __init__(self, session, word):
    method hits (line 315) | def hits(self):
    method append (line 318) | def append(self, *eids):
    method remove (line 322) | def remove(self, eids):
    method _WordSig (line 327) | def _WordSig(cls, word, config):
  class OldPostingList (line 336) | class OldPostingList(object):
    method _Optimize (line 345) | def _Optimize(cls, session, idx, force=False):
    method _Append (line 349) | def _Append(cls, session, word, mail_ids, compact=True, sig=None):
    method Lock (line 383) | def Lock(cls, lock, method, *args, **kwargs):
    method Optimize (line 388) | def Optimize(cls, *args, **kwargs):
    method Append (line 392) | def Append(cls, *args, **kwargs):
    method WordSig (line 396) | def WordSig(cls, word, config):
    method SaveFile (line 403) | def SaveFile(cls, session, prefix):
    method GetFile (line 407) | def GetFile(cls, session, sig, mode='r'):
    method __init__ (line 427) | def __init__(self, session, word, sig=None, config=None):
    method _parse_lines (line 437) | def _parse_lines(self, lines):
    method load (line 448) | def load(self):
    method _fmt_file (line 460) | def _fmt_file(self, prefix):
    method _compact (line 472) | def _compact(self, prefix, output):
    method save (line 490) | def save(self, prefix=None, compact=True, mode='wb'):
    method hits (line 519) | def hits(self):
    method append (line 522) | def append(self, eid):
    method remove (line 529) | def remove(self, eids):
  class GlobalPostingList (line 539) | class GlobalPostingList(OldPostingList):
    method _Optimize (line 542) | def _Optimize(cls, session, idx,
    method SaveFile (line 596) | def SaveFile(cls, session, prefix):
    method GetFile (line 600) | def GetFile(cls, session, sig, mode='r'):
    method _Append (line 608) | def _Append(cls, session, word, mail_ids, compact=True):
    method GetMaxMsgIdxPos (line 622) | def GetMaxMsgIdxPos(cls):
    method _unlocked_GetMaxMsgIdxPos (line 627) | def _unlocked_GetMaxMsgIdxPos(cls):
    method UpdateMaxMsgMid (line 639) | def UpdateMaxMsgMid(cls, session, msg_mids):
    method __init__ (line 663) | def __init__(self, *args, **kwargs):
    method _fmt_file (line 668) | def _fmt_file(self, prefix):
    method _compact (line 671) | def _compact(self, prefix, output, **kwargs):
    method load (line 674) | def load(self):
    method _migrate (line 684) | def _migrate(self, sig=None, compact=True):
    method _purge_deleted (line 696) | def _purge_deleted(self, sig, deleted_sig, deleted_set):
    method remove (line 703) | def remove(self, eids):
    method hits (line 707) | def hits(self):
    method plc_keys (line 711) | def plc_keys(self):

FILE: mailpile/safe_popen.py
  function PresetSafePopenArgs (line 44) | def PresetSafePopenArgs(**kwargs):
  class Safe_Pipe (line 56) | class Safe_Pipe(object):
    method __init__ (line 64) | def __init__(self):
    method write (line 69) | def write(self, *args, **kwargs):
    method read (line 72) | def read(self, *args, **kwargs):
    method close (line 75) | def close(self):
  class Safe_Popen (line 80) | class Safe_Popen(Unsafe_Popen):
    method _preset_args (line 81) | def _preset_args(self):
    method __init__ (line 87) | def __init__(self, args, bufsize=0,
    method _SAFE_POPEN_unlock (line 220) | def _SAFE_POPEN_unlock(self):
    method communicate (line 228) | def communicate(self, *args, **kwargs):
    method wait (line 233) | def wait(self, *args, **kwargs):
    method __del__ (line 238) | def __del__(self):
  function MakePopenUnsafe (line 248) | def MakePopenUnsafe():
  function MakePopenSafe (line 252) | def MakePopenSafe():

FILE: mailpile/search.py
  class MailIndex (line 39) | class MailIndex(BaseIndex):
    method __init__ (line 57) | def __init__(self, config):
    method l2m (line 78) | def l2m(self, line):
    method m2l (line 93) | def m2l(self, message):
    method load (line 99) | def load(self, session=None):
    method update_msg_tags (line 226) | def update_msg_tags(self, msg_idx_pos, msg_info):
    method _maybe_encrypt (line 236) | def _maybe_encrypt(self, data):
    method save_changes (line 257) | def save_changes(self, session=None):
    method save (line 315) | def save(self, session=None):
    method update_ptrs_and_msgids (line 361) | def update_ptrs_and_msgids(self, session):
    method _remove_location (line 376) | def _remove_location(self, session, msg_ptr):
    method _update_location (line 387) | def _update_location(self, session, msg_idx_pos, msg_ptr):
    method _extract_date_ts (line 403) | def _extract_date_ts(self, session, msg_mid, msg_id, msg, default):
    method _get_scan_progress (line 412) | def _get_scan_progress(self, mailbox_idx, event=None, reset=False):
    method scan_mailbox (line 437) | def scan_mailbox(self, session, mailbox_idx, mailbox_fn, mailbox_opener,
    method scan_one_message (line 582) | def scan_one_message(self, session, mailbox_idx, mbox, msg_mbox_key,
    method _real_scan_one (line 594) | def _real_scan_one(self, session,
    method edit_msg_info (line 667) | def edit_msg_info(self, msg_info,
    method _extract_header_info (line 714) | def _extract_header_info(self, msg):
    method _extract_info_and_index (line 723) | def _extract_info_and_index(self, session, mailbox_idx,
    method _index_incoming_message (line 751) | def _index_incoming_message(self, session,
    method index_email (line 803) | def index_email(self, session, email):
    method set_conversation_ids (line 846) | def set_conversation_ids(self, msg_mid, msg, subject_threading=True):
    method unthread_message (line 1013) | def unthread_message(self, msg_mid, new_subject=None):
    method add_new_msg (line 1116) | def add_new_msg(self, msg_ptr, msg_id, msg_ts, msg_from,
    method add_new_ghost (line 1152) | def add_new_ghost(self, msg_id, trash=False, subject=None):
    method filter_keywords (line 1168) | def filter_keywords(self, session, msg_mid, msg, keywords, incoming=Tr...
    method apply_filters (line 1194) | def apply_filters(self, session, filter_on, msg_mids=None, msg_idxs=No...
    method _list_header_keywords (line 1208) | def _list_header_keywords(self, hdr, val_lower, body_info):
    method read_message (line 1234) | def read_message(self, session,
    method clean_snippet (line 1513) | def clean_snippet(self, snippet):
    method index_message (line 1521) | def index_message(self, session, msg_mid, msg_id,
    method get_msg_at_idx_pos_uncached (line 1572) | def get_msg_at_idx_pos_uncached(self, msg_idx):
    method delete_msg_at_idx_pos (line 1578) | def delete_msg_at_idx_pos(self, session, msg_idx, keep_msgid=False):
    method update_msg_sorting (line 1620) | def update_msg_sorting(self, msg_idx, msg_info):
    method set_msg_at_idx_pos (line 1624) | def set_msg_at_idx_pos(self, msg_idx, msg_info, original_line=None):
    method get_conversation (line 1654) | def get_conversation(self, msg_info=None, msg_idx=None, ghosts=False):
    method get_replies (line 1679) | def get_replies(self, msg_info=None, msg_idx=None):
    method get_tags (line 1685) | def get_tags(self, msg_info=None, msg_idx=None):
    method add_tag (line 1693) | def add_tag(self, session, tag_id,
    method remove_tag (line 1776) | def remove_tag(self, session, tag_id,
    method search_tag (line 1838) | def search_tag(self, session, term, hits, recursion=0):
    method search (line 1852) | def search(self, session, searchterms,
    method _freshness_sorter (line 2026) | def _freshness_sorter(self, msg_info):
    method _prepare_sorting (line 2042) | def _prepare_sorting(self):
    method sort_results (line 2049) | def sort_results(self, session, results, how):

FILE: mailpile/search_history.py
  class SearchHistory (line 13) | class SearchHistory(object):
    method Load (line 26) | def Load(cls, config, merge=None):
    method __init__ (line 36) | def __init__(self):
    method save (line 40) | def save(self, config):
    method _compress (line 47) | def _compress(self, results, order):
    method _decompress (line 55) | def _decompress(self, compressed_bitmask):
    method add (line 59) | def add(self, terms, results, order):
    method get (line 73) | def get(self, session, fprint):
    method expire (line 84) | def expire(self, ttl=None, compact=None):

FILE: mailpile/security.py
  function GetUserSecret (line 28) | def GetUserSecret(workdir):
  function _lockdown (line 46) | def _lockdown(config):
  function in_disk_lockdown (line 62) | def in_disk_lockdown(config):
  function _lockdown_minimal (line 73) | def _lockdown_minimal(config):
  function _lockdown_config (line 80) | def _lockdown_config(config):
  function _lockdown_quit (line 90) | def _lockdown_quit(config):
  function _lockdown_basic (line 98) | def _lockdown_basic(config):
  function _lockdown_strict (line 103) | def _lockdown_strict(config):
  function forbid_command (line 149) | def forbid_command(command_obj, cc_list=None, config=None):
  function forbid_config_change (line 163) | def forbid_config_change(config, config_key):
  function secure_urlget (line 176) | def secure_urlget(session, url,
  function http_content_security_policy (line 218) | def http_content_security_policy(http_server):
  function make_csrf_token (line 231) | def make_csrf_token(secret, session_id, ts=None):
  function valid_csrf_token (line 241) | def valid_csrf_token(secret, session_id, csrf_token):
  function stretch_with_pbkdf2 (line 268) | def stretch_with_pbkdf2(password, salt, params):
  function stretch_with_scrypt (line 277) | def stretch_with_scrypt(password, salt, params):
  class SecurePassphraseStorage (line 303) | class SecurePassphraseStorage(object):
    method __init__ (line 349) | def __init__(self, passphrase=None, stretched=False):
    method copy (line 359) | def copy(self, src):
    method is_set (line 365) | def is_set(self):
    method stretches (line 368) | def stretches(self, salt, params=None):
    method stretched (line 394) | def stretched(self, salt, params=None):
    method set_passphrase (line 398) | def set_passphrase(self, passphrase):
    method compare (line 406) | def compare(self, passphrase):
    method read_byte_at (line 413) | def read_byte_at(self, offset):
    method get_passphrase (line 418) | def get_passphrase(self):
    method get_reader (line 423) | def get_reader(self):
  function tls_sock_cert_sha256 (line 460) | def tls_sock_cert_sha256(sock=None, cert=None):
  function tls_configure (line 476) | def tls_configure(sock, context, args, kwargs):
  function tls_new_context (line 551) | def tls_new_context():
  function tls_cert_tofu (line 558) | def tls_cert_tofu(wrapped, accept_certs, sname):
  function tls_context_wrap_socket (line 573) | def tls_context_wrap_socket(org_wrap, context, sock, *args, **kwargs):
  function tls_wrap_socket (line 583) | def tls_wrap_socket(org_wrap, sock, *args, **kwargs):
  function evaluate_sender_trust (line 595) | def evaluate_sender_trust(config, email, tree):
  function add_tls_context (line 753) | def add_tls_context(unused_org_wrap, sock, *args, **kwargs):

FILE: mailpile/smtp_client.py
  function sha512_512k (line 24) | def sha512_512k(data):
  function sha512_512kCheck (line 43) | def sha512_512kCheck(challenge, bits, solution):
  function sha512_512kCollide (line 50) | def sha512_512kCollide(challenge, bits, callback1k=None):
  function SMTorP_HashCash (line 70) | def SMTorP_HashCash(rcpt, msg, callback1k=None):
  class SMTP (line 83) | class SMTP(smtplib.SMTP):
  class SMTP_SSL (line 87) | class SMTP_SSL(smtplib.SMTP_SSL):
  class SendMailError (line 93) | class SendMailError(IOError):
    method __init__ (line 94) | def __init__(self, msg, details=None):
  function _RouteTuples (line 99) | def _RouteTuples(session, from_to_msg_ev_tuples, test_route=None):
  function SendMail (line 137) | def SendMail(session, msg_mid, from_to_msg_ev_tuples,

FILE: mailpile/spambayes/Options.py
  function load_options (line 1266) | def load_options():
  function get_pathname_option (line 1331) | def get_pathname_option(section, option):

FILE: mailpile/spambayes/OptionsClass.py
  class TypesWrapper (line 109) | class TypesWrapper(object):
  class Option (line 121) | class Option(object):
    method __init__ (line 122) | def __init__(self, name, nice_name="", default=None,
    method display_name (line 134) | def display_name(self):
    method default (line 137) | def default(self):
    method doc (line 140) | def doc(self):
    method valid_input (line 143) | def valid_input(self):
    method no_restore (line 146) | def no_restore(self):
    method set (line 149) | def set(self, val):
    method get (line 152) | def get(self):
    method multiple_values_allowed (line 155) | def multiple_values_allowed(self):
    method is_valid (line 159) | def is_valid(self, value):
    method is_valid_multiple (line 169) | def is_valid_multiple(self, value):
    method is_valid_single (line 179) | def is_valid_single(self, value):
    method _split_values (line 209) | def _split_values(self, value):
    method as_nice_string (line 232) | def as_nice_string(self, section=None):
    method as_documentation_string (line 247) | def as_documentation_string(self, section=None):
    method write_config (line 267) | def write_config(self, file):
    method convert (line 274) | def convert(self, value):
    method _convert (line 311) | def _convert(self, value, to_type):
    method unconvert (line 324) | def unconvert(self):
    method is_boolean (line 384) | def is_boolean(self):
  class OptionsClass (line 407) | class OptionsClass(object):
    method __init__ (line 408) | def __init__(self):
    method update_file (line 431) | def update_file(self, filename):
    method _add_missing (line 514) | def _add_missing(self, out, written, sect, vi, label=True):
    method load_defaults (line 531) | def load_defaults(self, defaults):
    method set_restore_point (line 550) | def set_restore_point(self):
    method revert_to_restore_point (line 561) | def revert_to_restore_point(self):
    method merge_files (line 572) | def merge_files(self, file_list):
    method convert_and_set (line 576) | def convert_and_set(self, section, option, value):
    method merge_file (line 580) | def merge_file(self, filename):
    method display_name (line 614) | def display_name(self, sect, opt):
    method default (line 617) | def default(self, sect, opt):
    method doc (line 620) | def doc(self, sect, opt):
    method valid_input (line 623) | def valid_input(self, sect, opt):
    method no_restore (line 626) | def no_restore(self, sect, opt):
    method is_valid (line 629) | def is_valid(self, sect, opt, value):
    method multiple_values_allowed (line 632) | def multiple_values_allowed(self, sect, opt):
    method is_boolean (line 636) | def is_boolean(self, sect, opt):
    method convert (line 640) | def convert(self, sect, opt, value):
    method unconvert (line 644) | def unconvert(self, sect, opt):
    method get_option (line 648) | def get_option(self, sect, opt):
    method get (line 654) | def get(self, sect, opt):
    method __getitem__ (line 660) | def __getitem__(self, key):
    method set (line 663) | def set(self, sect, opt, val=None):
    method set_from_cmdline (line 692) | def set_from_cmdline(self, arg, stream=None):
    method _report_deprecated_error (line 710) | def _report_deprecated_error(self, sect, opt):
    method _report_option_error (line 716) | def _report_option_error(self, sect, opt, val, stream, msg):
    method __setitem__ (line 738) | def __setitem__(self, key, value):
    method sections (line 741) | def sections(self):
    method options_in_section (line 750) | def options_in_section(self, section):
    method options (line 759) | def options(self, prepend_section_name=False):
    method display (line 771) | def display(self, add_comments=False):
    method _display_nice (line 794) | def _display_nice(self, section, option, formatter):
    method display_full (line 817) | def display_full(self, section=None, option=None):
    method output_for_docs (line 821) | def output_for_docs(self, section=None, option=None):

FILE: mailpile/spambayes/Tester.py
  class Test (line 3) | class Test:
    method __init__ (line 23) | def __init__(self):
    method set_classifier (line 27) | def set_classifier(self, classifier):
    method reset_test_results (line 30) | def reset_test_results(self):
    method train (line 49) | def train(self, hamstream=None, spamstream=None):
    method untrain (line 61) | def untrain(self, hamstream=None, spamstream=None):
    method predict (line 81) | def predict(self, stream, is_spam, callback=None):
    method false_positive_rate (line 115) | def false_positive_rate(self):
    method false_negative_rate (line 119) | def false_negative_rate(self):
    method unsure_rate (line 123) | def unsure_rate(self):
    method false_positives (line 127) | def false_positives(self):
    method false_negatives (line 130) | def false_negatives(self):
    method unsures (line 133) | def unsures(self):
  class _Example (line 136) | class _Example:
    method __init__ (line 137) | def __init__(self, name, words):
    method __iter__ (line 140) | def __iter__(self):

FILE: mailpile/spambayes/chi2.py
  function chi2Q (line 5) | def chi2Q(x2, v, exp=_math.exp, min=min):
  function normZ (line 23) | def normZ(z, sqrt2pi=_math.sqrt(2.0*_math.pi), exp=_math.exp):
  function normP (line 27) | def normP(z):
  function normIQ (line 59) | def normIQ(p, sqrt=_math.sqrt, ln=_math.log):
  function normIP (line 84) | def normIP(p):
  function main (line 93) | def main():
  function showscore (line 169) | def showscore(ps, ln=_math.log, ln2=_math.log(2), frexp=_math.frexp):

FILE: mailpile/spambayes/classifier.py
  class WordInfo (line 51) | class WordInfo(object):
    method __init__ (line 63) | def __init__(self):
    method __repr__ (line 66) | def __repr__(self):
    method __getstate__ (line 69) | def __getstate__(self):
    method __setstate__ (line 72) | def __setstate__(self, t):
  class Classifier (line 76) | class Classifier:
    method __init__ (line 91) | def __init__(self):
    method __getstate__ (line 96) | def __getstate__(self):
    method __setstate__ (line 99) | def __setstate__(self, t):
    method chi2_spamprob (line 124) | def chi2_spamprob(self, wordstream, evidence=False):
    method learn (line 196) | def learn(self, wordstream, is_spam):
    method unlearn (line 207) | def unlearn(self, wordstream, is_spam):
    method probability (line 216) | def probability(self, record):
    method _add_msg (line 297) | def _add_msg(self, wordstream, is_spam):
    method _remove_msg (line 318) | def _remove_msg(self, wordstream, is_spam):
    method _post_training (line 345) | def _post_training(self):
    method _getclues (line 359) | def _getclues(self, wordstream):
    method _worddistanceget (line 433) | def _worddistanceget(self, word):
    method _wordinfoget (line 442) | def _wordinfoget(self, word):
    method _wordinfoset (line 445) | def _wordinfoset(self, word, record):
    method _wordinfodel (line 448) | def _wordinfodel(self, word):
    method _enhance_wordstream (line 451) | def _enhance_wordstream(self, wordstream):
    method _wordinfokeys (line 479) | def _wordinfokeys(self):

FILE: mailpile/spambayes/safepickle.py
  function pickle_read (line 12) | def pickle_read(filename):
  function pickle_write (line 21) | def pickle_write(filename, value, protocol=0):

FILE: mailpile/tests/__init__.py
  function get_mailpile_root (line 29) | def get_mailpile_root():
  function _initialize_mailpile_for_testing (line 46) | def _initialize_mailpile_for_testing(workdir, test_data):
  function get_shared_mailpile (line 77) | def get_shared_mailpile():
  function capture (line 106) | def capture():
  class MailPileUnittest (line 118) | class MailPileUnittest(unittest.TestCase):
    method __init__ (line 119) | def __init__(self, *args, **kwargs):
    method setUpClass (line 123) | def setUpClass(cls):

FILE: mailpile/tests/data/pgp-data/buildexamples.py
  function getSourceFiles (line 7) | def getSourceFiles():
  function getProcesses (line 10) | def getProcesses():
  function runPGP (line 14) | def runPGP(input, params):
  function genExamples (line 27) | def genExamples():

FILE: mailpile/tests/gui/__init__.py
  class ElementHasClass (line 19) | class ElementHasClass(object):
    method __init__ (line 20) | def __init__(self, locator_tuple, class_name):
    method __call__ (line 24) | def __call__(self, driver):
  class ElementHasNotClass (line 32) | class ElementHasNotClass(object):
    method __init__ (line 33) | def __init__(self, locator_tuple, class_name):
    method __call__ (line 37) | def __call__(self, driver):
  class SeleniumScreenshotOnExceptionAspecter (line 45) | class SeleniumScreenshotOnExceptionAspecter(type):
    method __new__ (line 66) | def __new__(mcs, name, bases, dict):
    method wrap_method (line 77) | def wrap_method(mcs, method):
  class MailpileSeleniumTest (line 108) | class MailpileSeleniumTest(MailPileUnittest):
    method __init__ (line 134) | def __init__(self, *args, **kwargs):
    method setUp (line 137) | def setUp(self):
    method tearDown (line 140) | def tearDown(self):
    method _get_mailpile_sspec (line 148) | def _get_mailpile_sspec(cls):
    method _get_mailpile_url (line 153) | def _get_mailpile_url(cls):
    method _start_web_server (line 157) | def _start_web_server(cls):
    method _start_selenium_driver (line 165) | def _start_selenium_driver(cls):
    method _stop_selenium_driver (line 174) | def _stop_selenium_driver(cls):
    method setUpClass (line 183) | def setUpClass(cls):
    method _stop_web_server (line 190) | def _stop_web_server(cls):
    method tearDownClass (line 198) | def tearDownClass(cls):
    method go_to_mailpile_home (line 204) | def go_to_mailpile_home(self):
    method take_screenshot (line 207) | def take_screenshot(self, filename):
    method dump_source_to (line 213) | def dump_source_to(self, filename):
    method navigate_to (line 217) | def navigate_to(self, name):
    method submit_form (line 223) | def submit_form(self, form_id):
    method fill_form_field (line 227) | def fill_form_field(self, field, text):
    method assert_link_with_text (line 231) | def assert_link_with_text(self, text):
    method click_element_with_link_text (line 237) | def click_element_with_link_text(self, text):
    method click_element_with_id (line 243) | def click_element_with_id(self, element_id):
    method click_element_with_class (line 246) | def click_element_with_class(self, class_name):
    method page_title (line 249) | def page_title(self):
    method find_element_by_id (line 252) | def find_element_by_id(self, id):
    method find_element_containing_text (line 255) | def find_element_containing_text(self, text):
    method find_element_by_xpath (line 258) | def find_element_by_xpath(self, xpath):
    method find_element_by_class_name (line 261) | def find_element_by_class_name(self, class_name):
    method assert_text (line 264) | def assert_text(self, text):
    method wait_until_element_is_visible (line 267) | def wait_until_element_is_visible(self, element_id):
    method wait_until_element_is_visible_by_locator (line 270) | def wait_until_element_is_visible_by_locator(self, locator_tuple):
    method wait_until_element_is_invisible_by_locator (line 274) | def wait_until_element_is_invisible_by_locator(self, locator_tuple):
    method wait_until_element_has_class (line 278) | def wait_until_element_has_class(self, locator_tuple, class_name):
    method wait_until_element_has_not_class (line 281) | def wait_until_element_has_not_class(self, locator_tuple, class_name):
    method wait_for_element_condition (line 284) | def wait_for_element_condition(self, expected_conditions):

FILE: mailpile/tests/gui/test_contacts.py
  class ContactsGuiTest (line 4) | class ContactsGuiTest(MailpileSeleniumTest):
    method test_add_new_contact (line 5) | def test_add_new_contact(self):

FILE: mailpile/tests/gui/test_mail.py
  class MailGuiTest (line 4) | class MailGuiTest(MailpileSeleniumTest):
    method test_read_mail (line 5) | def test_read_mail(self):

FILE: mailpile/tests/gui/test_tags.py
  class TagGuiTest (line 9) | class TagGuiTest(MailpileSeleniumTest):
    method test_mark_read_unread (line 10) | def test_mark_read_unread(self):
    method _click_on_visible_element_with_class_name (line 27) | def _click_on_visible_element_with_class_name(self, class_name):
    method _toggle_tag_bar (line 32) | def _toggle_tag_bar(self):
    method _assert_element_has_class (line 37) | def _assert_element_has_class(self, element_id, class_name):
    method _assert_element_not_class (line 40) | def _assert_element_not_class(self, element_id, class_name):

FILE: mailpile/tests/test_command.py
  class TestCommands (line 10) | class TestCommands(MailPileUnittest):
    method test_index (line 11) | def test_index(self):
    method test_search (line 15) | def test_search(self):
    method test_optimize (line 20) | def test_optimize(self):
    method test_set (line 24) | def test_set(self):
    method test_unset (line 29) | def test_unset(self):
    method test_add (line 34) | def test_add(self):
    method test_add_mailbox_already_in_pile (line 38) | def test_add_mailbox_already_in_pile(self):
    method test_add_mailbox_no_such_directory (line 42) | def test_add_mailbox_no_such_directory(self):
    method test_output (line 46) | def test_output(self):
    method test_help (line 50) | def test_help(self):
    method test_help_variables (line 54) | def test_help_variables(self):
    method test_help_with_param_search (line 58) | def test_help_with_param_search(self):
    method test_help_urlmap_as_text (line 62) | def test_help_urlmap_as_text(self):
    method test_crypto_policy_action (line 67) | def test_crypto_policy_action(self):
    method test_reply_no_subject (line 74) | def test_reply_no_subject(self):
    method test_reply_subjects_on_first_msg (line 83) | def test_reply_subjects_on_first_msg(self):
    method test_reply_subject_on_reply_msg (line 91) | def test_reply_subject_on_reply_msg(self):
    method test_reply_subject_on_fw_msg (line 99) | def test_reply_subject_on_fw_msg(self):
    method test_fwd_subject_on_re_msg (line 107) | def test_fwd_subject_on_re_msg(self):
    method test_fw_subject_on_fw_msg (line 115) | def test_fw_subject_on_fw_msg(self):
  class TestCommandResult (line 124) | class TestCommandResult(MailPileUnittest):
    method test_command_result_as_dict (line 125) | def test_command_result_as_dict(self):
    method test_command_result_as_text (line 129) | def test_command_result_as_text(self):
    method test_command_result_as_text_for_boolean_result (line 133) | def test_command_result_as_text_for_boolean_result(self):
    method test_command_result_non_zero (line 139) | def test_command_result_non_zero(self):
    method test_command_result_as_json (line 143) | def test_command_result_as_json(self):
    method test_command_result_as_html (line 147) | def test_command_result_as_html(self):
  class TestTagging (line 152) | class TestTagging(MailPileUnittest):
    method test_addtag (line 153) | def test_addtag(self):
  class TestGPG (line 157) | class TestGPG(MailPileUnittest):
    method test_key_search (line 158) | def test_key_search(self):
    method test_key_receive (line 177) | def test_key_receive(self):
    method test_key_import (line 194) | def test_key_import(self):
    method test_nicknym_get_key (line 199) | def test_nicknym_get_key(self):
    method test_nicknym_refresh_key (line 202) | def test_nicknym_refresh_key(self):

FILE: mailpile/tests/test_config.py
  class TestConfig (line 10) | class TestConfig(MailPileUnittest):
    method test_BoolCheck_trues (line 15) | def test_BoolCheck_trues(self):
    method test_BoolCheck_falses (line 20) | def test_BoolCheck_falses(self):
    method test_BoolCheck_exception (line 25) | def test_BoolCheck_exception(self):
    method test_RouteProtocolCheck_valid (line 32) | def test_RouteProtocolCheck_valid(self):
    method test_RouteProtocolCheck_invalid (line 43) | def test_RouteProtocolCheck_invalid(self):
    method test_HostNameValid_ipv4 (line 51) | def test_HostNameValid_ipv4(self):
    method test_HostNameValid_ipv6 (line 60) | def test_HostNameValid_ipv6(self):
    method test_HostNameValid_hostname (line 70) | def test_HostNameValid_hostname(self):
    method test_HostNameCheck_valid (line 85) | def test_HostNameCheck_valid(self):
    method test_HostNameCheck_invalid (line 95) | def test_HostNameCheck_invalid(self):
    method test_HostNameCheck_non_socket_errors_still_raised (line 114) | def test_HostNameCheck_non_socket_errors_still_raised(self):
    method test_SlugCheck_valid (line 120) | def test_SlugCheck_valid(self):
    method test_SlugCheck_invalid (line 126) | def test_SlugCheck_invalid(self):
    method test_SlashSlugCheck (line 134) | def test_SlashSlugCheck(self):
    method test_B36Check (line 143) | def test_B36Check(self):
    method test_B36Check (line 149) | def test_B36Check(self):
    method test_PathCheck_valid (line 160) | def test_PathCheck_valid(self):
    method test_PathCheck_invalid (line 171) | def test_PathCheck_invalid(self):
    method test_FileCheck_valid (line 184) | def test_FileCheck_valid(self):
    method test_FileCheck_invalid (line 195) | def test_FileCheck_invalid(self):
    method test_DirCheck_valid (line 208) | def test_DirCheck_valid(self):
    method test_DirCheck_invalid (line 219) | def test_DirCheck_invalid(self):
    method test_NewPathCheck_valid (line 232) | def test_NewPathCheck_valid(self):
    method test_NewPathCheck_invalid (line 244) | def test_NewPathCheck_invalid(self):
    method test_UrlCheck_valid (line 254) | def test_UrlCheck_valid(self):
    method test_UrlCheck_invalid (line 265) | def test_UrlCheck_invalid(self):
    method test_EmailCheck_valid (line 279) | def test_EmailCheck_valid(self):
    method test_EmailCheck_invalid (line 294) | def test_EmailCheck_invalid(self):
    method test_GPGKeyCheck_valid (line 305) | def test_GPGKeyCheck_valid(self):
    method test_GPGKeyCheck_invalid (line 321) | def test_GPGKeyCheck_invalid(self):

FILE: mailpile/tests/test_crypto_policy.py
  class CryptoPolicyBaseTest (line 8) | class CryptoPolicyBaseTest(MailPileUnittest):
    method setUp (line 9) | def setUp(self):
    method _add_vcard (line 13) | def _add_vcard(self, full_name, email):
  class UpdateCryptoPolicyForUserTest (line 20) | class UpdateCryptoPolicyForUserTest(CryptoPolicyBaseTest):
    method test_args_are_checked (line 21) | def test_args_are_checked(self):
    method test_policies_are_validated (line 27) | def test_policies_are_validated(self):
    method test_vcard_has_to_exist (line 42) | def test_vcard_has_to_exist(self):
    method test_vcard_is_updated (line 47) | def test_vcard_is_updated(self):
  class CryptoPolicyForUserTest (line 54) | class CryptoPolicyForUserTest(CryptoPolicyBaseTest):
    method test_no_email_provided (line 55) | def test_no_email_provided(self):
    method test_no_msg_with_email_ (line 59) | def test_no_msg_with_email_(self):
    method test_with_signed_email (line 64) | def test_with_signed_email(self):
    method test_with_encrypted_email (line 69) | def test_with_encrypted_email(self):
    method test_vcard_overrides_mail_history (line 74) | def test_vcard_overrides_mail_history(self):

FILE: mailpile/tests/test_eventlog.py
  class TestEventlog (line 18) | class TestEventlog(MailPileUnittest):
    method setUp (line 20) | def setUp(self):
    method tearDown (line 23) | def tearDown(self):
    method test_NewEventId (line 30) | def test_NewEventId(self):
    method test_ClassName (line 42) | def test_ClassName(self):
    method test_ClassName_unicode (line 50) | def test_ClassName_unicode(self):
    method test_event_Parse (line 71) | def test_event_Parse(self):
    method test_event_Parse_invalid (line 85) | def test_event_Parse_invalid(self):
    method test_event_as_dict (line 89) | def test_event_as_dict(self):
    method test_event_as_dict_no_private (line 102) | def test_event_as_dict_no_private(self):
    method test_event_as_json (line 109) | def test_event_as_json(self):
    method test_event_as_json_no_private (line 123) | def test_event_as_json_no_private(self):
    method test_event_as_html (line 133) | def test_event_as_html(self):
    method test_eventlog (line 146) | def test_eventlog(self):

FILE: mailpile/tests/test_keylookup.py
  class KeylookupBaseTest (line 30) | class KeylookupBaseTest(MailPileUnittest):
    method setUp (line 31) | def setUp(self):
  class KeylookupDNSPKILookup (line 35) | class KeylookupDNSPKILookup(KeylookupBaseTest):
    method test_lookup_dnspki (line 36) | def test_lookup_dnspki(self):
  class KeylookupPGPKeyserverLookup (line 47) | class KeylookupPGPKeyserverLookup(KeylookupBaseTest):
    method test_lookup_pgpkeyserver (line 48) | def test_lookup_pgpkeyserver(self):
  class KeylookupEmailLookup (line 56) | class KeylookupEmailLookup(KeylookupBaseTest):
    method test_lookup_emailkeys (line 57) | def test_lookup_emailkeys(self):
  class KeylookupOverallTest (line 64) | class KeylookupOverallTest(KeylookupBaseTest):
    method test_lookup (line 65) | def test_lookup(self):

FILE: mailpile/tests/test_mail_generator.py
  class TestMailGenerator (line 9) | class TestMailGenerator(MailPileUnittest):
    method test_is8bitstring (line 11) | def test_is8bitstring(self):
    method test_make_boundary (line 18) | def test_make_boundary(self):

FILE: mailpile/tests/test_mailutils.py
  class TestCommands (line 8) | class TestCommands(MailPileUnittest):
    method test_decode_header_no_encoding (line 9) | def test_decode_header_no_encoding(self):

FILE: mailpile/tests/test_performance.py
  function checkSearch (line 7) | def checkSearch(postinglist_kb, query):
  function test_generator (line 19) | def test_generator():

FILE: mailpile/tests/test_plugin.py
  class PluginTest (line 8) | class PluginTest(MailPileUnittest):
    method setUp (line 9) | def setUp(self):
    method tearDown (line 16) | def tearDown(self):
    method test_plugin_is_discovered (line 19) | def test_plugin_is_discovered(self):
    method test_code_files_are_loaded_in_order (line 25) | def test_code_files_are_loaded_in_order(self):
    method _create_manifest (line 55) | def _create_manifest(self, name, json_data):
    method _create_manifest_json (line 64) | def _create_manifest_json(self, name):
    method _create_code_file (line 76) | def _create_code_file(self, content, filename):

FILE: mailpile/tests/test_search.py
  function checkSearch (line 8) | def checkSearch(query, expected_count=1):
  function test_generator (line 23) | def test_generator():

FILE: mailpile/tests/test_ui.py
  class TestUI (line 9) | class TestUI(MailPileUnittest):
    method _ui_swap (line 10) | def _ui_swap(self):
    method test_ui_debug_log_debug_not_set (line 14) | def test_ui_debug_log_debug_not_set(self):
    method test_ui_debug_log_debug_set (line 24) | def test_ui_debug_log_debug_set(self):
    method test_ui_log_block (line 35) | def test_ui_log_block(self):
    method test_ui_clear_log (line 66) | def test_ui_clear_log(self):
    method test_ui_display_result_text (line 85) | def test_ui_display_result_text(self):

FILE: mailpile/tests/test_vcard.py
  class TestVCard (line 6) | class TestVCard(MailPileUnittest):
    method test_VCardLine_with_args (line 8) | def test_VCardLine_with_args(self):
    method test_VCardLine_no_args (line 12) | def test_VCardLine_no_args(self):
    method test_VCardLine_args_too_long (line 18) | def test_VCardLine_args_too_long(self): # when input is greater than 7...
    method test_VCardLine_with_vcard_data (line 24) | def test_VCardLine_with_vcard_data(self):
    method test_VCardLine_set_line_id (line 30) | def test_VCardLine_set_line_id(self):
    method test_VCardLine_set_attr (line 35) | def test_VCardLine_set_attr(self):
    method test_VCardLine_Quote (line 45) | def test_VCardLine_Quote(self):
    method test_VCardLine_ParseLine_unquoted (line 53) | def test_VCardLine_ParseLine_unquoted(self):
    method test_VCardLine_ParseLine_quoted (line 61) | def test_VCardLine_ParseLine_quoted(self):

FILE: mailpile/tests/test_vcard_mork.py
  class TestVCard (line 8) | class TestVCard(MailPileUnittest):
    method test_hexcmp (line 10) | def test_hexcmp(self):

FILE: mailpile/ui.py
  class SuppressHtmlOutput (line 36) | class SuppressHtmlOutput(Exception):
  function default_dict (line 40) | def default_dict(*args):
  class NoColors (line 53) | class NoColors:
    method __init__ (line 72) | def __init__(self):
    method __enter__ (line 78) | def __enter__(self, *args, **kwargs):
    method __exit__ (line 81) | def __exit__(self, *args, **kwargs):
    method color (line 84) | def color(self, text, color='', weight='', readline=False):
    method replace_line (line 88) | def replace_line(self, text, chars=None):
    method clean (line 93) | def clean(self, text):
    method write (line 96) | def write(self, data):
    method buffer (line 100) | def buffer(self, data):
    method check_max_width (line 113) | def check_max_width(self):
  class ANSIColors (line 117) | class ANSIColors(NoColors):
    method __init__ (line 140) | def __init__(self):
    method replace_line (line 144) | def replace_line(self, text, chars=None):
    method clean (line 149) | def clean(self, text):
    method check_max_width (line 152) | def check_max_width(self):
  class Completer (line 164) | class Completer(o
Condensed preview — 624 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (5,666K chars).
[
  {
    "path": ".coveragerc",
    "chars": 1090,
    "preview": "# =========================================================================\n# COVERAGE CONFIGURATION FILE: .coveragerc\n#"
  },
  {
    "path": ".dockerignore",
    "chars": 23,
    "preview": "Dockerfile\nVagrantfile\n"
  },
  {
    "path": ".gitignore",
    "chars": 476,
    "preview": "*.pyc\n*.pyo\n*~\n*.po\n*.debhelper\n*.iml\n*.ipr\n*.swp\n*.swo\nmp-virtualenv\n.idea\n.*deps\n.tox\nmailpile.egg-info/\nmailpile-tmp."
  },
  {
    "path": ".gitmodules",
    "chars": 464,
    "preview": "[submodule \"doc\"]\n\tpath = doc\n\turl = https://github.com/pagekite/Mailpile.wiki.git\n\tbranch = master\n[submodule \"shared-d"
  },
  {
    "path": ".travis.yml",
    "chars": 194,
    "preview": "language: python\npython: 2.7\nenv:\n  - TOX_ENV=py27\n\naddons:\n  apt:\n    packages:\n      - gnupg\n\ninstall:\n  - pip install"
  },
  {
    "path": ".tx/config",
    "chars": 201,
    "preview": "[main]\nhost = https://www.transifex.com\n\n[mailpile.mailpilepot]\nsource_file = shared-data/locale/mailpile.pot\nsource_lan"
  },
  {
    "path": "AGPLv3.txt",
    "chars": 34594,
    "preview": "NOTE: Please see the file COPYING.md for details on Mailpile licensing.\n\n\n                    GNU AFFERO GENERAL PUBLIC "
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3727,
    "preview": "# The Mailpile Code of Conduct\n\nThe Mailpile project depends on its community of volunteers, supporters and of\ncourse us"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 4952,
    "preview": "## How to contribute to Mailpile\n\nFirst of all: Thank you! :heart:\n\n\n#### Further Reading\n\nSecond of all, please adhere "
  },
  {
    "path": "COPYING.md",
    "chars": 1327,
    "preview": "# Mailpile - a program for doing stuff with e-mail\n\nCopyright (C) 2011-2015, Bjarni R. Einarsson, Mailpile ehf and frien"
  },
  {
    "path": "DEV_FAQ.md",
    "chars": 12118,
    "preview": "## Mailpile Developer FAQ\n\nThis document contain a collection of frequently asked questions (with\nanswers) about Mailpil"
  },
  {
    "path": "Dockerfile",
    "chars": 810,
    "preview": "FROM debian:stretch-slim\n\nENV GID 33411\nENV UID 33411\n\nRUN apt-get update && \\\n    apt-get install -y curl apt-transport"
  },
  {
    "path": "Dockerfile.dev",
    "chars": 616,
    "preview": "FROM mailpile\n\n# Install C compiler for python deps w/ native extensions\nRUN apk --no-cache add \\\n  gcc \\\n  libc-dev \\\n "
  },
  {
    "path": "Gruntfile.js",
    "chars": 2862,
    "preview": "module.exports = function(grunt) {\n\n  grunt.registerTask('watch', [ 'watch' ]);\n\n  grunt.initConfig({\n    concat: {\n    "
  },
  {
    "path": "MANIFEST.in",
    "chars": 635,
    "preview": "recursive-exclude locale *\nrecursive-exclude scripts *\nrecursive-exclude testing *\nrecursive-include mailpile/tests/data"
  },
  {
    "path": "Makefile",
    "chars": 9626,
    "preview": "# Recipes for stuff\nexport PYTHONPATH := .\n\nhelp:\n\t@echo \"\"\n\t@echo \"BUILD\"\n\t@echo \"    dpkg\"\n\t@echo \"        Create a de"
  },
  {
    "path": "README.md",
    "chars": 3346,
    "preview": "# Welcome to Mailpile! #\n\n**IMPORTANT NOTE**\n\nDevelopment on this codebase has halted, until the\n[Python3 rewrite](https"
  },
  {
    "path": "babel.cfg",
    "chars": 857,
    "preview": "# Extraction from Python source files\n[python: mailpile/**.py]\nencoding = utf-8\n\n[python: shared-data/contrib/**.py]\nenc"
  },
  {
    "path": "bower.json",
    "chars": 940,
    "preview": "{\n  \"name\": \"mailpile\",\n  \"version\": \"0.1.0\",\n  \"homepage\": \"https://mailpile.is\",\n  \"authors\": [\n    \"Various\"\n  ],\n  \""
  },
  {
    "path": "docker-compose.dev.yml",
    "chars": 304,
    "preview": "version: '3.0'\nservices:\n  mailpile_dev:\n    tty: true\n    stdin_open: true\n    container_name: mailpile_dev\n    build:\n"
  },
  {
    "path": "docker-compose.yml",
    "chars": 206,
    "preview": "version: '3.0'\nservices:\n  mailpile:\n    container_name: mailpile\n    build: .\n    image: mailpile\n    volumes:\n      - "
  },
  {
    "path": "install_hooks.py",
    "chars": 437,
    "preview": "import os\nimport sys\n\ndef symlink_develop(config):\n    if 'develop' in sys.argv:\n        share_path = os.path.join(sys.p"
  },
  {
    "path": "mailpile/__init__.py",
    "chars": 2494,
    "preview": "from mailpile.i18n import gettext as _\nfrom mailpile.i18n import ngettext as _n\n\n\n__all__ = ['Mailpile',\n           \"app"
  },
  {
    "path": "mailpile/__main__.py",
    "chars": 118,
    "preview": "import sys\nfrom mailpile.app import Main\n\n\ndef main():\n    Main(sys.argv[1:])\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "mailpile/app.py",
    "chars": 11848,
    "preview": "from __future__ import print_function\nimport getopt\nimport gettext\nimport locale\nimport os\nimport re\nimport sys\nimport t"
  },
  {
    "path": "mailpile/auth.py",
    "chars": 20808,
    "preview": "import time\nfrom urlparse import parse_qs, urlparse\nfrom urllib import quote, urlencode\n\nfrom mailpile.commands import C"
  },
  {
    "path": "mailpile/command_cache.py",
    "chars": 7356,
    "preview": "import time\n\nimport mailpile.util\nfrom mailpile.commands import Command\nfrom mailpile.eventlog import Event\nfrom mailpil"
  },
  {
    "path": "mailpile/commands.py",
    "chars": 27022,
    "preview": "# The basic Mailpile command framework.\n#\n# TODO: Merge with plugins/ the division is obsolete and artificial.\n#\nimport "
  },
  {
    "path": "mailpile/config/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "mailpile/config/base.py",
    "chars": 28586,
    "preview": "from __future__ import print_function\nimport io\nimport json\nimport os\nimport ConfigParser\nfrom urllib import quote, unqu"
  },
  {
    "path": "mailpile/config/defaults.py",
    "chars": 16627,
    "preview": "from __future__ import print_function\nAPPVER = \"1.0.0rc6\"\nABOUT = \"\"\"\\\nMailpile.py              a tool             Copyr"
  },
  {
    "path": "mailpile/config/detect.py",
    "chars": 187,
    "preview": "try:\n    import ssl\nexcept ImportError:\n    ssl = None\n\ntry:\n    import sockschain as socks\nexcept ImportError:\n    try:"
  },
  {
    "path": "mailpile/config/manager.py",
    "chars": 63165,
    "preview": "from __future__ import print_function\nimport copy\nimport cPickle\nimport io\nimport jinja2\nimport json\nimport os\nimport so"
  },
  {
    "path": "mailpile/config/paths.py",
    "chars": 2938,
    "preview": "import os\nimport sys\n\nimport mailpile.platforms\n\ntry:\n    from appdirs import AppDirs\nexcept ImportError:\n    AppDirs = "
  },
  {
    "path": "mailpile/config/validators.py",
    "chars": 9009,
    "preview": "from __future__ import print_function\nimport os\nimport socket\nimport re\n\ntry:\n    import win_inet_pton\nexcept ImportErro"
  },
  {
    "path": "mailpile/conn_brokers.py",
    "chars": 38835,
    "preview": "from __future__ import print_function\n# Connection brokers facilitate & manage incoming and outgoing connections.\n#\n# Th"
  },
  {
    "path": "mailpile/crypto/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "mailpile/crypto/aes_utils.py",
    "chars": 5665,
    "preview": "from __future__ import print_function\n# This is a compatibility wrapper for using whatever AES library is handy.\n# By de"
  },
  {
    "path": "mailpile/crypto/autocrypt.py",
    "chars": 17822,
    "preview": "from __future__ import print_function\n# Copyright (C) 2018 Jack Dodds & Mailpile ehf.\n# This code is part of Mailpile an"
  },
  {
    "path": "mailpile/crypto/gpgi.py",
    "chars": 78599,
    "preview": "#coding:utf-8\nfrom __future__ import print_function\nimport os\nimport string\nimport sys\nimport time\nimport re\nimport Stri"
  },
  {
    "path": "mailpile/crypto/keyinfo.py",
    "chars": 13956,
    "preview": "from __future__ import print_function\nimport time\nimport traceback\n\nimport pgpdump\nimport pgpdump.packet\nfrom pgpdump.ut"
  },
  {
    "path": "mailpile/crypto/mime.py",
    "chars": 32595,
    "preview": "from __future__ import print_function\n# These are methods to do with MIME and crypto, implementing PGP/MIME.\n\nimport mat"
  },
  {
    "path": "mailpile/crypto/records.py",
    "chars": 32875,
    "preview": "#\n# NOTE: THIS CODE IS NOT BEING USED - YET.\n#       IT IS HERE TO FACILITATE REVIEW, COMMENTS AND EXPERIMENTS.\n#\n# FIXM"
  },
  {
    "path": "mailpile/crypto/state.py",
    "chars": 5260,
    "preview": "from __future__ import print_function\n#. Common crypto state and structure\nimport copy\n\nfrom mailpile.i18n import gettex"
  },
  {
    "path": "mailpile/crypto/streamer.py",
    "chars": 44881,
    "preview": "from __future__ import print_function\n#\n# This is code to stream data to or from encrypted storage. If the invoking\n# co"
  },
  {
    "path": "mailpile/crypto/tor.py",
    "chars": 10026,
    "preview": "import copy\nimport socket\nimport subprocess\nimport threading\nimport time\nimport traceback\nimport re\nimport sys\nimport os"
  },
  {
    "path": "mailpile/eventlog.py",
    "chars": 15026,
    "preview": "from __future__ import print_function\nimport copy\nimport datetime\nimport json\nimport os\nimport threading\nimport time\nfro"
  },
  {
    "path": "mailpile/httpd.py",
    "chars": 26014,
    "preview": "#\n# Mailpile's built-in HTTPD\n#\n###############################################################################\nimport C"
  },
  {
    "path": "mailpile/i18n.py",
    "chars": 5845,
    "preview": "import gettext\nimport os\nimport threading\nfrom gettext import translation, gettext, NullTranslations\nfrom jinja2 import "
  },
  {
    "path": "mailpile/index/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "mailpile/index/base.py",
    "chars": 8813,
    "preview": "from __future__ import print_function\nimport copy\nimport json\nimport random\nimport rfc822\nimport time\nimport traceback\n\n"
  },
  {
    "path": "mailpile/index/mailboxes.py",
    "chars": 2276,
    "preview": "from __future__ import print_function\nimport email.parser\nimport json\nimport traceback\n\nfrom mailpile.util import *\nfrom"
  },
  {
    "path": "mailpile/index/msginfo.py",
    "chars": 1012,
    "preview": "from __future__ import print_function\nfrom mailpile.i18n import gettext as _\nfrom mailpile.i18n import ngettext as _n\n\n\n"
  },
  {
    "path": "mailpile/index/search.py",
    "chars": 1859,
    "preview": "from __future__ import print_function\nimport time\n\nfrom mailpile.i18n import gettext as _\nfrom mailpile.i18n import nget"
  },
  {
    "path": "mailpile/mail_source/__init__.py",
    "chars": 48667,
    "preview": "import datetime\nimport os\nimport random\nimport re\nimport thread\nimport threading\nimport traceback\nimport time\n\nimport ma"
  },
  {
    "path": "mailpile/mail_source/imap.py",
    "chars": 46081,
    "preview": "from __future__ import print_function\n# This implements our IMAP mail source. It has been tested against the\n# following"
  },
  {
    "path": "mailpile/mail_source/imap_starttls.py",
    "chars": 1027,
    "preview": "import imaplib\nimport ssl\n\nCommands = {\n    'STARTTLS': ('NONAUTH')\n}\n\nimaplib.Commands.update(Commands)\n\nclass IMAP4(im"
  },
  {
    "path": "mailpile/mail_source/imap_utf7.py",
    "chars": 1628,
    "preview": "# -*- coding: utf-8- -*-\n\n# from: http://piao-tech.blogspot.no/2010/03/get-offlineimap-working-with-non-ascii.html#resou"
  },
  {
    "path": "mailpile/mail_source/local.py",
    "chars": 6248,
    "preview": "import time\nimport os\n\nfrom mailpile.mail_source import BaseMailSource\nfrom mailpile.i18n import gettext as _\nfrom mailp"
  },
  {
    "path": "mailpile/mail_source/pop3.py",
    "chars": 6567,
    "preview": "import os\nimport ssl\nimport traceback\n\nfrom mailpile.auth import IndirectPassword\nfrom mailpile.conn_brokers import Mast"
  },
  {
    "path": "mailpile/mailboxes/__init__.py",
    "chars": 7323,
    "preview": "## Dear hackers!\n##\n## It would be great to have more mailbox classes.  They should be derived\n## from or implement the "
  },
  {
    "path": "mailpile/mailboxes/gmvault.py",
    "chars": 1815,
    "preview": "import mailbox\nimport os\nimport gzip\nimport rfc822\n\nimport mailpile.mailboxes\nimport mailpile.mailboxes.maildir as maild"
  },
  {
    "path": "mailpile/mailboxes/macmail.py",
    "chars": 5038,
    "preview": "import mailbox\nimport sys\nimport os\nimport warnings\nimport rfc822\nimport time\nimport errno\n\nimport mailpile.mailboxes\nfr"
  },
  {
    "path": "mailpile/mailboxes/maildir.py",
    "chars": 1614,
    "preview": "import mailbox\nimport os\nimport sys\n\nimport mailpile.mailboxes\nfrom mailpile.i18n import gettext as _\nfrom mailpile.i18n"
  },
  {
    "path": "mailpile/mailboxes/maildirwin.py",
    "chars": 374,
    "preview": "import mailpile.mailboxes\nimport mailpile.mailboxes.maildir as maildir\n\nfrom mailpile.i18n import gettext as _\nfrom mail"
  },
  {
    "path": "mailpile/mailboxes/mbox.py",
    "chars": 17575,
    "preview": "from __future__ import print_function\nimport errno\nimport mailbox\nimport os\nimport re\nimport threading\nimport time\nimpor"
  },
  {
    "path": "mailpile/mailboxes/pop3.py",
    "chars": 13406,
    "preview": "from __future__ import print_function\ntry:\n    import cStringIO as StringIO\nexcept ImportError:\n    import StringIO\n\nimp"
  },
  {
    "path": "mailpile/mailboxes/wervd.py",
    "chars": 6819,
    "preview": "import email.generator\nimport email.message\nimport mailbox\nimport StringIO\nimport sys\n\nimport mailpile.mailboxes\nfrom ma"
  },
  {
    "path": "mailpile/mailutils/__init__.py",
    "chars": 653,
    "preview": "# vim: set fileencoding=utf-8 :\n#\nMBX_ID_LEN = 4  # 4x36 == 1.6 million mailboxes\n\n\ndef FormatMbxId(n):\n    if not isins"
  },
  {
    "path": "mailpile/mailutils/addresses.py",
    "chars": 14756,
    "preview": "# vim: set fileencoding=utf-8 :\n#\nfrom __future__ import print_function\nimport base64\nimport copy\nimport quopri\nimport r"
  },
  {
    "path": "mailpile/mailutils/emails.py",
    "chars": 60136,
    "preview": "# vim: set fileencoding=utf-8 :\n#\n# FIXME: Refactor this monster into mailpile.mailutils.*\n#\nfrom __future__ import prin"
  },
  {
    "path": "mailpile/mailutils/generator.py",
    "chars": 17952,
    "preview": "# Copyright (C) 2001-2010 Python Software Foundation\n# Contact: email-sig@python.org\n#\n# Updated/forked January 2014 by "
  },
  {
    "path": "mailpile/mailutils/header.py",
    "chars": 4896,
    "preview": "# vim: set fileencoding=utf-8 :\n\"\"\" Backport of Python > 3.3 email.header.decode_header()\n\nIt includes fixes that have n"
  },
  {
    "path": "mailpile/mailutils/headerprint.py",
    "chars": 4610,
    "preview": "# vim: set fileencoding=utf-8 :\n#\nfrom __future__ import print_function\nimport re\nfrom mailpile.util import md5_hex\n\n\nMU"
  },
  {
    "path": "mailpile/mailutils/html.py",
    "chars": 3861,
    "preview": "# vim: set fileencoding=utf-8 :\n#\nfrom __future__ import print_function\nimport lxml.etree\nimport lxml.html\nimport lxml.h"
  },
  {
    "path": "mailpile/mailutils/safe.py",
    "chars": 6821,
    "preview": "from __future__ import print_function\nimport email\nimport email.errors\nimport email.message\nimport random\nimport re\nimpo"
  },
  {
    "path": "mailpile/mailutils/vcal.py",
    "chars": 4508,
    "preview": "from __future__ import print_function\nimport time\nimport icalendar\nfrom datetime import datetime\n\ndef calendar_parse(pay"
  },
  {
    "path": "mailpile/packing.py",
    "chars": 9426,
    "preview": "from __future__ import print_function\nimport struct\nimport time\nimport zlib\n\nfrom mailpile.util import *\n\n\ndef PackIntSe"
  },
  {
    "path": "mailpile/platforms.py",
    "chars": 6077,
    "preview": "\"\"\"\nThis module tries to centralize most of the platform-specific code in use by\nMailpile. If you find yourself checking"
  },
  {
    "path": "mailpile/plugins/__init__.py",
    "chars": 32549,
    "preview": "from __future__ import print_function\n# Plugins!\nimport imp\nimport inspect\nimport json\nimport os\nimport sys\nimport trace"
  },
  {
    "path": "mailpile/plugins/autotag.py",
    "chars": 16894,
    "preview": "# This is the generic auto-tagging plugin.\n#\n# We feed the classifier the same terms as go into the search engine,\n# whi"
  },
  {
    "path": "mailpile/plugins/autotag_sb.py",
    "chars": 1409,
    "preview": "# Add SpamBayes as an option to the autotagger. We like SpamBayes.\n#\n# We feed the classifier the same terms as go into "
  },
  {
    "path": "mailpile/plugins/backups.py",
    "chars": 14683,
    "preview": "from __future__ import print_function\nimport cStringIO\nimport datetime\nimport gzip\nimport json\nimport os\nimport sys\nimpo"
  },
  {
    "path": "mailpile/plugins/compose.py",
    "chars": 46070,
    "preview": "import datetime\nimport email.utils\nimport os\nimport os.path\nimport re\nimport traceback\n\nimport mailpile.security as secu"
  },
  {
    "path": "mailpile/plugins/contacts.py",
    "chars": 63551,
    "preview": "import os\nimport random\nimport time\nfrom email import encoders\n\nimport mailpile.config.defaults\nimport mailpile.security"
  },
  {
    "path": "mailpile/plugins/core.py",
    "chars": 83165,
    "preview": "# These are the Mailpile core commands, the public \"API\" we expose for\n# searching, tagging and editing e-mail.\n#\n# FIXM"
  },
  {
    "path": "mailpile/plugins/crypto_autocrypt.py",
    "chars": 23732,
    "preview": "# This file contains Autocrypt application logic: state database,\n# persistance, integration with Mailpile itself.\n#\n# L"
  },
  {
    "path": "mailpile/plugins/crypto_gnupg.py",
    "chars": 27586,
    "preview": "from __future__ import print_function\nimport copy\nimport datetime\nimport re\nimport time\nimport urllib2\nfrom email import"
  },
  {
    "path": "mailpile/plugins/crypto_policy.py",
    "chars": 11279,
    "preview": "from datetime import datetime, timedelta\n\nimport mailpile.security as security\nfrom mailpile.i18n import gettext as _\nfr"
  },
  {
    "path": "mailpile/plugins/cryptostate.py",
    "chars": 3322,
    "preview": "from __future__ import print_function\nfrom mailpile.i18n import gettext as _\nfrom mailpile.i18n import ngettext as _n\nfr"
  },
  {
    "path": "mailpile/plugins/dates.py",
    "chars": 3430,
    "preview": "import time\nimport datetime\n\nfrom mailpile.plugins import PluginManager\nfrom mailpile.i18n import gettext as _\nfrom mail"
  },
  {
    "path": "mailpile/plugins/eventlog.py",
    "chars": 7871,
    "preview": "import time\n\nimport mailpile.util\nimport mailpile.security as security\nfrom mailpile.commands import Command\nfrom mailpi"
  },
  {
    "path": "mailpile/plugins/exporters.py",
    "chars": 5056,
    "preview": "import mailbox\nimport os\nimport time\n\nimport mailpile.security as security\nfrom mailpile.commands import Command\nfrom ma"
  },
  {
    "path": "mailpile/plugins/groups.py",
    "chars": 2929,
    "preview": "from mailpile.commands import Command\nfrom mailpile.i18n import gettext as _\nfrom mailpile.i18n import ngettext as _n\nfr"
  },
  {
    "path": "mailpile/plugins/gui.py",
    "chars": 11195,
    "preview": "import json\nimport select\nimport socket\nimport threading\nimport time\n\nimport mailpile.auth\nimport mailpile.util\nfrom mai"
  },
  {
    "path": "mailpile/plugins/html_magic.py",
    "chars": 6725,
    "preview": "# This plugin generates Javascript, HTML or CSS fragments based on the\n# current theme, skin and active plugins.\n#\n# It "
  },
  {
    "path": "mailpile/plugins/keylookup/__init__.py",
    "chars": 35692,
    "preview": "# FIXME: We would like to implement the following priority for sources of keys:\n#\n#       1. Local validated        +Use"
  },
  {
    "path": "mailpile/plugins/keylookup/email_keylookup.py",
    "chars": 5757,
    "preview": "import datetime\nimport time\nimport copy\n\nfrom mailpile.crypto.autocrypt import *\nfrom mailpile.crypto.keyinfo import get"
  },
  {
    "path": "mailpile/plugins/keylookup/wkd.py",
    "chars": 9887,
    "preview": "import hashlib\nimport ssl\nimport urllib\nimport urllib2\n\nfrom mailpile.security import secure_urlget\nfrom mailpile.comman"
  },
  {
    "path": "mailpile/plugins/migrate.py",
    "chars": 8476,
    "preview": "import mailpile.security as security\nfrom mailpile.commands import Command\nfrom mailpile.config.defaults import APPVER\nf"
  },
  {
    "path": "mailpile/plugins/motd.py",
    "chars": 6003,
    "preview": "import os\nimport json\nimport sys\nfrom datetime import datetime as dtime\nfrom urllib2 import urlopen\n\nfrom mailpile.comma"
  },
  {
    "path": "mailpile/plugins/oauth.py",
    "chars": 10141,
    "preview": "from __future__ import print_function\nimport json\nimport time\nimport traceback\nfrom urllib import urlencode, quote_plus\n"
  },
  {
    "path": "mailpile/plugins/plugins.py",
    "chars": 3904,
    "preview": "import os\n\nimport mailpile.commands\nimport mailpile.security as security\nfrom mailpile.i18n import gettext as _\nfrom mai"
  },
  {
    "path": "mailpile/plugins/search.py",
    "chars": 49118,
    "preview": "import datetime\nimport json\nimport re\nimport time\nimport traceback\nimport unicodedata\n\nimport mailpile.security as secur"
  },
  {
    "path": "mailpile/plugins/setup_magic.py",
    "chars": 55978,
    "preview": "from __future__ import print_function\nimport copy\nimport datetime\nimport os\nimport random\nimport socket\nimport sys\nimpor"
  },
  {
    "path": "mailpile/plugins/setup_magic_ispdb.py",
    "chars": 2851,
    "preview": "##[ Static ISPDB entries ]######################################################\n\n# https://autoconfig.thunderbird.net/v"
  },
  {
    "path": "mailpile/plugins/sizes.py",
    "chars": 2203,
    "preview": "import math\nimport time\nimport datetime\n\nfrom mailpile.i18n import gettext as _\nfrom mailpile.i18n import ngettext as _n"
  },
  {
    "path": "mailpile/plugins/smtp_server.py",
    "chars": 7840,
    "preview": "import asyncore\nimport email.parser\nimport random\nimport smtpd\nimport threading\nimport traceback\n\nimport mailpile.securi"
  },
  {
    "path": "mailpile/plugins/tags.py",
    "chars": 42280,
    "preview": "# -*- coding: utf-8 -*-\nimport mailpile.security as security\nfrom mailpile.commands import Command\nfrom mailpile.i18n im"
  },
  {
    "path": "mailpile/plugins/vcard_carddav.py",
    "chars": 4667,
    "preview": "#coding:utf-8\nimport base64\nimport httplib\nimport sys\nimport re\nimport getopt\nfrom lxml import etree\n\nfrom mailpile.i18n"
  },
  {
    "path": "mailpile/plugins/vcard_gnupg.py",
    "chars": 11083,
    "preview": "#coding:utf-8\nimport os\n\nimport mailpile.security as security\nfrom mailpile.commands import Command\nfrom mailpile.eventl"
  },
  {
    "path": "mailpile/plugins/vcard_gravatar.py",
    "chars": 5441,
    "preview": "#coding:utf-8\nimport os\nimport random\nimport time\n\nimport mailpile.util\nfrom mailpile.i18n import gettext as _\nfrom mail"
  },
  {
    "path": "mailpile/plugins/vcard_libravatar.py",
    "chars": 5454,
    "preview": "#coding:utf-8\nimport random\nimport time\n\nimport mailpile.util\nfrom mailpile.i18n import gettext as _\nfrom mailpile.plugi"
  },
  {
    "path": "mailpile/plugins/vcard_mork.py",
    "chars": 10945,
    "preview": "#!/usr/bin/env python2.7\n#coding:utf-8\nfrom __future__ import print_function\nimport sys\nimport re\nimport getopt\nfrom sys"
  },
  {
    "path": "mailpile/plugins/webterminal.py",
    "chars": 4299,
    "preview": "import json\nimport random\n\nfrom mailpile.app import FriendlyPipeTransform\nfrom mailpile.commands import *\nfrom mailpile."
  },
  {
    "path": "mailpile/postinglist.py",
    "chars": 24268,
    "preview": "from __future__ import print_function\nimport os\nimport sys\nimport random\nimport threading\nimport traceback\nimport time\n\n"
  },
  {
    "path": "mailpile/safe_popen.py",
    "chars": 8945,
    "preview": "from __future__ import print_function\n#\n# This module implements a safer version of Popen and a safe wrapper around\n# os"
  },
  {
    "path": "mailpile/search.py",
    "chars": 92152,
    "preview": "from __future__ import print_function\nimport cStringIO\nimport email\nimport random\nimport re\nimport rfc822\nimport time\nim"
  },
  {
    "path": "mailpile/search_history.py",
    "chars": 4029,
    "preview": "import time\nimport zlib\n\nimport mailpile.util\nfrom mailpile.i18n import gettext as _\nfrom mailpile.i18n import ngettext "
  },
  {
    "path": "mailpile/security.py",
    "chars": 26654,
    "preview": "\"\"\"\nGlobal Mailpile crypto/privacy/security policy\n\nThis module attempts to collect in one place all of the different\nse"
  },
  {
    "path": "mailpile/smtp_client.py",
    "chars": 16022,
    "preview": "# -*- coding: utf-8 -*-\nimport random\nimport hashlib\nimport smtplib\nimport socket\nimport ssl\nimport sys\nimport time\n\nimp"
  },
  {
    "path": "mailpile/spambayes/LICENSE.txt",
    "chars": 2649,
    "preview": "Copyright (C) 2002-2007 Python Software Foundation; All Rights Reserved\n\nThe Python Software Foundation (PSF) holds copy"
  },
  {
    "path": "mailpile/spambayes/Options.py",
    "chars": 63666,
    "preview": "\"\"\"Options\n\nAbstract:\n\nOptions.options is a globally shared options object.\nThis object is initialised when the module i"
  },
  {
    "path": "mailpile/spambayes/OptionsClass.py",
    "chars": 34461,
    "preview": "\"\"\"OptionsClass\n\nClasses:\n    Option - Holds information about an option\n    OptionsClass - A collection of options\n\nAbs"
  },
  {
    "path": "mailpile/spambayes/Tester.py",
    "chars": 7012,
    "preview": "from mailpile.spambayes.Options import options\n\nclass Test:\n    # Pass a classifier instance (an instance of Bayes).\n   "
  },
  {
    "path": "mailpile/spambayes/__init__.py",
    "chars": 102,
    "preview": "# package marker.\n\n__version__ = \"1.1b3\"\n__date__ = \"Nov 23, 2017\"\n\nfrom classifier import Classifier\n"
  },
  {
    "path": "mailpile/spambayes/chi2.py",
    "chars": 5367,
    "preview": "from __future__ import print_function\nimport math as _math\nimport random\n\ndef chi2Q(x2, v, exp=_math.exp, min=min):\n    "
  },
  {
    "path": "mailpile/spambayes/classifier.py",
    "chars": 19214,
    "preview": "#! /usr/bin/env python\n\nfrom __future__ import generators\nfrom __future__ import print_function\n\n# An implementation of "
  },
  {
    "path": "mailpile/spambayes/safepickle.py",
    "chars": 1804,
    "preview": "\"\"\"Lock pickle files for reading and writing.\"\"\"\n\nfrom __future__ import print_function\nimport sys\nimport os\nfrom six.mo"
  },
  {
    "path": "mailpile/tests/TESTS",
    "chars": 357,
    "preview": "Tests that need to be written:\n\n  - Search tests\n\t- Performance test [DONE?]\n\t- from:, to:, etc..\n\t- Keywords\n  - Indexi"
  },
  {
    "path": "mailpile/tests/__init__.py",
    "chars": 3331,
    "preview": "import contextlib\nimport os\nimport random\nimport shutil\nimport stat\nimport sys\nimport unittest\nfrom cStringIO import Str"
  },
  {
    "path": "mailpile/tests/data/Maildir/cur/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "mailpile/tests/data/Maildir/cur/1379857166.25979_1.hottie,2,S",
    "chars": 32581,
    "preview": "Return-path: <b0964ab49cbbre=example.com@bounce.twitter.com>\nEnvelope-to: bre@localhost\nDelivery-date: Mon, 16 Sep 2013 "
  },
  {
    "path": "mailpile/tests/data/Maildir/cur/1379857166.25979_3.hottie,2,S",
    "chars": 58215,
    "preview": "Return-path: <feministinn-bounces@listar.hi.is>\nEnvelope-to: bre@localhost\nDelivery-date: Mon, 16 Sep 2013 14:57:36 +000"
  },
  {
    "path": "mailpile/tests/data/Maildir/cur/1379857166.25979_5.hottie,2,S",
    "chars": 38646,
    "preview": "Return-path: <b0964ab49cbbre=example.com@bounce.twitter.com>\nEnvelope-to: bre@localhost\nDelivery-date: Mon, 16 Sep 2013 "
  },
  {
    "path": "mailpile/tests/data/Maildir/cur/1379857166.25979_7.hottie,2,S",
    "chars": 39672,
    "preview": "Return-path: <b0965969b92bre=example.com@bounce.twitter.com>\nEnvelope-to: bre@localhost\nDelivery-date: Thu, 19 Sep 2013 "
  },
  {
    "path": "mailpile/tests/data/Maildir/cur/1379857166.25979_9.hottie,2,S",
    "chars": 522403,
    "preview": "Return-path: <feministinn-bounces@listar.hi.is>\nEnvelope-to: bre@localhost\nDelivery-date: Thu, 19 Sep 2013 09:42:57 +000"
  },
  {
    "path": "mailpile/tests/data/Maildir/cur/1379857166.25980_9.hottie,2,S",
    "chars": 1136,
    "preview": "From nobody Fri Mar  7 15:17:43 2014\nContent-Type: multipart/signed; protocol=\"application/pgp-signature\";\n micalg=\"pgp-"
  },
  {
    "path": "mailpile/tests/data/Maildir/cur/1379857166.25981_10.hottie,2,S",
    "chars": 1911,
    "preview": "From nobody Fri Mar  7 17:36:01 2014\nEnvelope-to: encrypter@test.local\nContent-Type: multipart/encrypted; boundary=\"===="
  },
  {
    "path": "mailpile/tests/data/Maildir/cur/broken_email_1396694612",
    "chars": 36527,
    "preview": "Return-Path: <bounce-mc.us6_13990351.453669-hi=brennannovak.com@mail351.us3.mcdlv.net>\nX-Original-To: reverse@heart.reve"
  },
  {
    "path": "mailpile/tests/data/Maildir/cur/encrypted-empty-content-w-key.eml",
    "chars": 2786,
    "preview": "Message-ID: <569ACDC4.2020300@example.com>\nDate: Sun, 17 Jan 2016 00:09:56 +0100\nFrom: John Doe <test@example.com>\nUser-"
  },
  {
    "path": "mailpile/tests/data/Maildir/cur/fw-question-1443197278.mbx",
    "chars": 176,
    "preview": "From nobody Fri Mar  7 15:17:43 2014\nContent-Type: text/plain; charset=UTF-8\nMIME-Version: 1.0\nSubject: Fw: About shrubb"
  },
  {
    "path": "mailpile/tests/data/Maildir/cur/mailpile-1398950855.mbx",
    "chars": 61320,
    "preview": "From MAILER-DAEMON Thu May  1 13:27:35 2014\nReturn-Path: <smari@mailpile.is>\nDelivered-To: smari@smarimccarthy.is\nReceiv"
  },
  {
    "path": "mailpile/tests/data/Maildir/cur/mailpile-1400069992.mbx",
    "chars": 33254,
    "preview": "Return-Path: <bounces+hi=wigglebot.com@bounce-dyn.square.com>\nX-Original-To: reverse@heart.wigglecorp.com\nDelivered-To: "
  },
  {
    "path": "mailpile/tests/data/Maildir/cur/no-subject-1400067892.mbx",
    "chars": 150,
    "preview": "From nobody Fri Mar  7 15:17:43 2014\nContent-Type: text/plain; charset=UTF-8\nMIME-Version: 1.0\nFrom: ohcheeou@test.local"
  },
  {
    "path": "mailpile/tests/data/Maildir/new/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "mailpile/tests/data/Maildir/tmp/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "mailpile/tests/data/contacts/README",
    "chars": 133,
    "preview": "contacttest.vcf, contacttest.ldif and contacttest.mork are from:\n https://wiki.mozilla.org/Examples_of_various_Address_B"
  },
  {
    "path": "mailpile/tests/data/contacts/contacttest.ldif",
    "chars": 466,
    "preview": "dn: cn=Douglas Adams,mail=Douglas@douglasadams.com\nobjectclass: top\nobjectclass: person\nobjectclass: organizationalPerso"
  },
  {
    "path": "mailpile/tests/data/contacts/contacttest.mork",
    "chars": 1801,
    "preview": "// \n< <(a=c)> // (f=iso-8859-1)\n (B8=Custom4)(B9=Notes)(BA=LastModifiedDate)(BB=RecordKey)\n (BC=AddrCharSet)(BD=LastReco"
  },
  {
    "path": "mailpile/tests/data/contacts/contacttest.vcf",
    "chars": 463,
    "preview": "BEGIN:VCARD\nVERSION:3.0\nN:Adams;Douglas;;;\n FN:Douglas Adams\nNICKNAME:Douglas_adams\nEMAIL;type=INTERNET;type=HOME;type=p"
  },
  {
    "path": "mailpile/tests/data/gpg-keyring/testing.gpg.pub",
    "chars": 1053,
    "preview": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v1.4.14 (GNU/Linux)\n\nmI0EUtAPXgEEAK6KUVSqSE9lvWDYuIoNURj4K5Zq0GDW7FP"
  },
  {
    "path": "mailpile/tests/data/pgp-data/buildexamples.py",
    "chars": 1342,
    "preview": "from __future__ import print_function\nimport os\nimport email\nimport mailbox\nfrom subprocess import Popen, PIPE\n\ndef getS"
  },
  {
    "path": "mailpile/tests/data/pgp-data/sources/ar.iso-8859-6",
    "chars": 21,
    "preview": "       .           .\n"
  },
  {
    "path": "mailpile/tests/data/pgp-data/sources/ar.utf-8",
    "chars": 117,
    "preview": "يولد جميع الناس أحرارًا متساوين في الكرامة والحقوق. وقد وهبوا عقلاً وضميرًا وعليهم أن يعامل بعضهم بعضًا بروح الإخاء.\n"
  },
  {
    "path": "mailpile/tests/data/pgp-data/sources/ar.windows-1256",
    "chars": 21,
    "preview": "       .           .\n"
  },
  {
    "path": "mailpile/tests/data/pgp-data/sources/cn.utf-8",
    "chars": 43,
    "preview": "人人生而自由,在尊严和权利上一律平等。他们赋有理性和良心,并应以兄弟关系的精神相对待。"
  },
  {
    "path": "mailpile/tests/data/pgp-data/sources/gr.iso-8859-7",
    "chars": 28,
    "preview": "          .      ,         ."
  },
  {
    "path": "mailpile/tests/data/pgp-data/sources/gr.utf-8",
    "chars": 185,
    "preview": "Ολοι οι άνθρωποι γεννιούνται και ίσοι στην αξιοπρέπεια και στα δικαιώματα. Είναι προικισμένοι με λογική και συνείδηση, κ"
  },
  {
    "path": "mailpile/tests/data/pgp-data/sources/gr.windows-1253",
    "chars": 28,
    "preview": "          .      ,         ."
  },
  {
    "path": "mailpile/tests/data/pgp-data/sources/ru.koi8-ru",
    "chars": 28,
    "preview": "          .               .\n"
  },
  {
    "path": "mailpile/tests/data/pgp-data/sources/ru.utf-8",
    "chars": 161,
    "preview": "Все люди рождаются свободными и равными в своем достоинстве и правах. Они наделены разумом и совестью и должны поступать"
  },
  {
    "path": "mailpile/tests/data/pgp-data/sources/ru.windows-1251",
    "chars": 28,
    "preview": "          .               .\n"
  },
  {
    "path": "mailpile/tests/data/pgp-data/sources/vi.utf-8",
    "chars": 183,
    "preview": "Tất cả mọi người sinh ra đều được tự do và bình đẳng về nhân phẩm và quyền. Mọi con người đều được tạo hóa ban cho lý tr"
  },
  {
    "path": "mailpile/tests/data/pub.key",
    "chars": 3827,
    "preview": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v1\n\nmQINBFIy5N0BEAC95CiN7iaBRyO916KzYse7zAm4E5FgxEplfATgpJ8Cxx20d3cm"
  },
  {
    "path": "mailpile/tests/data/tests.mbx",
    "chars": 124361,
    "preview": "From kde-isl+owner@example.com Mon Sep 16 14:55:07 2013\nReturn-path: <kde-isl+owner@example.com>\nEnvelope-to: bre@localh"
  },
  {
    "path": "mailpile/tests/gui/__init__.py",
    "chars": 9694,
    "preview": "try:\n    from selenium import webdriver\n    from selenium.common.exceptions import WebDriverException, StaleElementRefer"
  },
  {
    "path": "mailpile/tests/gui/test_contacts.py",
    "chars": 610,
    "preview": "from mailpile.tests.gui import MailpileSeleniumTest\n\n\nclass ContactsGuiTest(MailpileSeleniumTest):\n    def test_add_new_"
  },
  {
    "path": "mailpile/tests/gui/test_mail.py",
    "chars": 658,
    "preview": "from mailpile.tests.gui import MailpileSeleniumTest\n\n\nclass MailGuiTest(MailpileSeleniumTest):\n    def test_read_mail(se"
  },
  {
    "path": "mailpile/tests/gui/test_tags.py",
    "chars": 1714,
    "preview": "try:\n    from selenium.webdriver.common.by import By\nexcept ImportError:\n    pass\n\nfrom mailpile.tests.gui import Mailpi"
  },
  {
    "path": "mailpile/tests/test_command.py",
    "chars": 7610,
    "preview": "import unittest\nimport os\nfrom mock import patch\n\nimport mailpile\nfrom mailpile.commands import Action as action\nfrom ma"
  },
  {
    "path": "mailpile/tests/test_config.py",
    "chars": 10412,
    "preview": "import unittest\nimport os\nimport mailpile\nimport mailpile.config.validators as validators\n\nfrom nose.tools import raises"
  },
  {
    "path": "mailpile/tests/test_crypto_policy.py",
    "chars": 3349,
    "preview": "from __future__ import print_function\nfrom mailpile.vcard import MailpileVCard, VCardLine\nfrom mailpile.tests import Mai"
  },
  {
    "path": "mailpile/tests/test_eventlog.py",
    "chars": 5870,
    "preview": "import unittest\nimport mailpile\nimport re\nimport json\nimport os\nimport threading\nimport time\n\nfrom nose.tools import rai"
  },
  {
    "path": "mailpile/tests/test_keylookup.py",
    "chars": 2587,
    "preview": "from mock import patch\nimport datetime\n\nfrom mailpile.tests import MailPileUnittest\nfrom mailpile.tests import get_share"
  },
  {
    "path": "mailpile/tests/test_mail_generator.py",
    "chars": 884,
    "preview": "# -*- coding: utf-8 -*-\r\nimport unittest\r\nimport mailpile\r\n\r\nfrom mailpile.tests import MailPileUnittest\r\n\r\nimport mailp"
  },
  {
    "path": "mailpile/tests/test_mailutils.py",
    "chars": 305,
    "preview": "import unittest\n\nimport mailpile\nfrom mailpile.tests import MailPileUnittest\nfrom mailpile.mailutils.header import decod"
  },
  {
    "path": "mailpile/tests/test_performance.py",
    "chars": 926,
    "preview": "import unittest\nfrom nose.tools import assert_equal, assert_less\n\nfrom mailpile.tests import get_shared_mailpile, MailPi"
  },
  {
    "path": "mailpile/tests/test_plugin.py",
    "chars": 2516,
    "preview": "import shutil\nimport os\nfrom os import path\nfrom mailpile.tests import MailPileUnittest\nimport json\n\n\nclass PluginTest(M"
  },
  {
    "path": "mailpile/tests/test_search.py",
    "chars": 1719,
    "preview": "from __future__ import print_function\nimport unittest\nfrom nose.tools import assert_equal, assert_less\n\nfrom mailpile.te"
  },
  {
    "path": "mailpile/tests/test_ui.py",
    "chars": 4362,
    "preview": "import json\nimport unittest\nimport mailpile\nfrom mailpile.ui import UserInteraction\n\nfrom mailpile.tests import capture,"
  },
  {
    "path": "mailpile/tests/test_vcard.py",
    "chars": 3010,
    "preview": "import unittest\nimport mailpile\n\nfrom mailpile.tests import MailPileUnittest\n\nclass TestVCard(MailPileUnittest):\n\n    de"
  },
  {
    "path": "mailpile/tests/test_vcard_mork.py",
    "chars": 674,
    "preview": "import unittest\r\nimport mailpile\r\n\r\nfrom mailpile.tests import MailPileUnittest\r\n\r\nfrom mailpile.plugins import vcard_mo"
  },
  {
    "path": "mailpile/ui.py",
    "chars": 29256,
    "preview": "#\n# This file contains the UserInteraction and Session classes.\n#\n# The Session encapsulates settings and command result"
  },
  {
    "path": "mailpile/urlmap.py",
    "chars": 29538,
    "preview": "from __future__ import print_function\nimport cgi\nimport time\nfrom urlparse import parse_qs, urlparse\nfrom urllib import "
  },
  {
    "path": "mailpile/util.py",
    "chars": 38187,
    "preview": "# coding: utf-8\n#\n# Misc. utility functions for Mailpile.\n#\nfrom __future__ import print_function\nimport cgi\nimport copy"
  },
  {
    "path": "mailpile/vcard.py",
    "chars": 60432,
    "preview": "from __future__ import print_function\nimport random\nimport threading\nimport time\n\nfrom markupsafe import escape\n\nimport "
  },
  {
    "path": "mailpile/vfs.py",
    "chars": 14754,
    "preview": "# This is a very simple virtual file system abstraction for Mailpile.\n#\n# The purpose of this code is not to be a genera"
  },
  {
    "path": "mailpile/workers.py",
    "chars": 13978,
    "preview": "from __future__ import print_function\nimport datetime\nimport random\nimport threading\nimport traceback\nimport time\n\nimpor"
  },
  {
    "path": "mailpile/www/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "mailpile/www/jinjaextensions.py",
    "chars": 40156,
    "preview": "import copy\nimport datetime\nimport hashlib\nimport random\nimport re\nimport urllib\nimport json\nimport shlex\nimport time\nfr"
  },
  {
    "path": "mailpile/www/jinjaloader.py",
    "chars": 885,
    "preview": "import os\n\nfrom jinja2 import BaseLoader, TemplateNotFound\n\n\nclass MailpileJinjaLoader(BaseLoader):\n    \"\"\"\n    A Jinja2"
  },
  {
    "path": "mp.cmd",
    "chars": 389,
    "preview": "@echo off\nset PATH=%PATH%;GnuPG\\;OpenSSL\\;\nset PYTHONPATH=%~dp0;%~dp0\\GnuPG\\;%~dp0\\OpenSSL\\;\nif exist python27\\python.ex"
  },
  {
    "path": "package.json",
    "chars": 246,
    "preview": "{\n  \"name\": \"mailpile\",\n  \"version\": \"0.4.1\",\n  \"devDependencies\": {\n    \"grunt\": \"latest\",\n    \"grunt-contrib-concat\": "
  },
  {
    "path": "packages/Dockerfile_debian",
    "chars": 1862,
    "preview": "# ----------------------------------------------------------------------------#\n# This Dockerfile creates a Debian packa"
  },
  {
    "path": "packages/debian/changelog",
    "chars": 145,
    "preview": "mailpile (<-- version -->) unstable; urgency=medium\n\n  * Automated build.\n\n -- Mailpile Team <team@mailpile.is>  Mon, 28"
  },
  {
    "path": "packages/debian/compat",
    "chars": 2,
    "preview": "9\n"
  },
  {
    "path": "packages/debian/control",
    "chars": 3249,
    "preview": "Source: mailpile\nMaintainer: Alexandre Viau <aviau@debian.org>\nSection: mail\nPriority: optional\nBuild-Depends: debhelper"
  },
  {
    "path": "packages/debian/copyright",
    "chars": 9962,
    "preview": "Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: mailpile\nSource: https://github"
  },
  {
    "path": "packages/debian/gbp.conf",
    "chars": 30,
    "preview": "[DEFAULT]\npristine-tar = True\n"
  }
]

// ... and 424 more files (download for full content)

About this extraction

This page contains the full source code of the mailpile/Mailpile GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 624 files (5.2 MB), approximately 1.4M tokens, and a symbol index with 4413 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!