summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKali Kaneko <kali@leap.se>2015-09-24 15:16:03 -0400
committerKali Kaneko <kali@leap.se>2015-09-24 15:16:03 -0400
commite714515718cc36fa1e31f9cf90a9a9728d4a4fbd (patch)
treef78d99e3ea38663c2841525f9a6e197e132925f6
parentbca46ee76f31a1272245156d3f2c6fcd4c7da180 (diff)
parent1b8e9f5d6df6aedd3566069d9d27adc1d8ad771d (diff)
Merge branch 'develop' into debian/experimental
-rw-r--r--changes/bug-7480_extract_attach_and_openpgp1
-rw-r--r--changes/feature-7471_disable-local-bind-for-docker1
-rw-r--r--docs/api/Makefile177
-rw-r--r--docs/api/conf.py331
-rw-r--r--docs/api/index.rst23
-rw-r--r--docs/api/leap.mail.adaptors.rst43
-rw-r--r--docs/api/leap.mail.adaptors.tests.rst28
-rw-r--r--docs/api/leap.mail.imap.rst52
-rw-r--r--docs/api/leap.mail.imap.service.rst9
-rw-r--r--docs/api/leap.mail.incoming.rst20
-rw-r--r--docs/api/leap.mail.outgoing.rst21
-rw-r--r--docs/api/leap.mail.plugins.rst20
-rw-r--r--docs/api/leap.mail.rst105
-rw-r--r--docs/api/leap.mail.smtp.rst19
-rw-r--r--docs/api/mail.imap.rst118
-rw-r--r--docs/api/mail.imap.service.rst30
-rw-r--r--docs/api/mail.imap.tests.rst38
-rw-r--r--docs/api/mail.rst70
-rw-r--r--docs/api/mail.smtp.rst37
-rw-r--r--docs/api/mail.smtp.tests.rst22
-rw-r--r--docs/api/make.bat242
-rw-r--r--docs/conf.py17
-rw-r--r--docs/index.rst66
-rw-r--r--docs/intro.rst4
-rwxr-xr-xdocs/recreate_apidocs.sh4
-rw-r--r--pkg/requirements-leap.pip2
-rw-r--r--src/leap/mail/imap/mailbox.py5
-rw-r--r--src/leap/mail/imap/server.py4
-rw-r--r--src/leap/mail/imap/service/imap.py14
-rwxr-xr-xsrc/leap/mail/imap/tests/getmail102
-rw-r--r--src/leap/mail/incoming/service.py94
-rw-r--r--src/leap/mail/incoming/tests/test_incoming_mail.py95
-rw-r--r--src/leap/mail/interfaces.py183
-rw-r--r--src/leap/mail/mail.py35
-rw-r--r--src/leap/mail/mailbox_indexer.py5
-rw-r--r--src/leap/mail/outgoing/service.py32
-rw-r--r--src/leap/mail/outgoing/tests/test_outgoing.py2
-rw-r--r--src/leap/mail/rfc3156.py (renamed from src/leap/mail/smtp/rfc3156.py)0
-rw-r--r--src/leap/mail/smtp/__init__.py15
-rw-r--r--src/leap/mail/smtp/gateway.py16
-rw-r--r--src/leap/mail/tests/__init__.py2
41 files changed, 837 insertions, 1267 deletions
diff --git a/changes/bug-7480_extract_attach_and_openpgp b/changes/bug-7480_extract_attach_and_openpgp
new file mode 100644
index 0000000..27f668a
--- /dev/null
+++ b/changes/bug-7480_extract_attach_and_openpgp
@@ -0,0 +1 @@
+- don't extract openpgp header if valid attached key (Closes: #7480)
diff --git a/changes/feature-7471_disable-local-bind-for-docker b/changes/feature-7471_disable-local-bind-for-docker
new file mode 100644
index 0000000..a1ccb67
--- /dev/null
+++ b/changes/feature-7471_disable-local-bind-for-docker
@@ -0,0 +1 @@
+- disable local only tcp bind on docker containers to allow access to IMAP and SMTP. Related to #7471.
diff --git a/docs/api/Makefile b/docs/api/Makefile
deleted file mode 100644
index ebcd0f4..0000000
--- a/docs/api/Makefile
+++ /dev/null
@@ -1,177 +0,0 @@
-# Makefile for Sphinx documentation
-#
-
-# You can set these variables from the command line.
-SPHINXOPTS =
-SPHINXBUILD = sphinx-build
-PAPER =
-BUILDDIR = _build
-
-# User-friendly check for sphinx-build
-ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
-$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
-endif
-
-# Internal variables.
-PAPEROPT_a4 = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
-
-help:
- @echo "Please use \`make <target>' where <target> is one of"
- @echo " html to make standalone HTML files"
- @echo " dirhtml to make HTML files named index.html in directories"
- @echo " singlehtml to make a single large HTML file"
- @echo " pickle to make pickle files"
- @echo " json to make JSON files"
- @echo " htmlhelp to make HTML files and a HTML help project"
- @echo " qthelp to make HTML files and a qthelp project"
- @echo " devhelp to make HTML files and a Devhelp project"
- @echo " epub to make an epub"
- @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
- @echo " latexpdf to make LaTeX files and run them through pdflatex"
- @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
- @echo " text to make text files"
- @echo " man to make manual pages"
- @echo " texinfo to make Texinfo files"
- @echo " info to make Texinfo files and run them through makeinfo"
- @echo " gettext to make PO message catalogs"
- @echo " changes to make an overview of all changed/added/deprecated items"
- @echo " xml to make Docutils-native XML files"
- @echo " pseudoxml to make pseudoxml-XML files for display purposes"
- @echo " linkcheck to check all external links for integrity"
- @echo " doctest to run all doctests embedded in the documentation (if enabled)"
-
-clean:
- rm -rf $(BUILDDIR)/*
-
-html:
- $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
- $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
- @echo
- @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
- $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
- @echo
- @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
- $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
- @echo
- @echo "Build finished; now you can process the pickle files."
-
-json:
- $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
- @echo
- @echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
- $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
- @echo
- @echo "Build finished; now you can run HTML Help Workshop with the" \
- ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
- $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
- @echo
- @echo "Build finished; now you can run "qcollectiongenerator" with the" \
- ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
- @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/mail.qhcp"
- @echo "To view the help file:"
- @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/mail.qhc"
-
-devhelp:
- $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
- @echo
- @echo "Build finished."
- @echo "To view the help file:"
- @echo "# mkdir -p $$HOME/.local/share/devhelp/mail"
- @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/mail"
- @echo "# devhelp"
-
-epub:
- $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
- @echo
- @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo
- @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
- @echo "Run \`make' in that directory to run these through (pdf)latex" \
- "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through pdflatex..."
- $(MAKE) -C $(BUILDDIR)/latex all-pdf
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-latexpdfja:
- $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
- @echo "Running LaTeX files through platex and dvipdfmx..."
- $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
- @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
- $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
- @echo
- @echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
- $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
- @echo
- @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-texinfo:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo
- @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
- @echo "Run \`make' in that directory to run these through makeinfo" \
- "(use \`make info' here to do that automatically)."
-
-info:
- $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
- @echo "Running Texinfo files through makeinfo..."
- make -C $(BUILDDIR)/texinfo info
- @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
-
-gettext:
- $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
- @echo
- @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
-
-changes:
- $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
- @echo
- @echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
- $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
- @echo
- @echo "Link check complete; look for any errors in the above output " \
- "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
- $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
- @echo "Testing of doctests in the sources finished, look at the " \
- "results in $(BUILDDIR)/doctest/output.txt."
-
-xml:
- $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
- @echo
- @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
-
-pseudoxml:
- $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
- @echo
- @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/docs/api/conf.py b/docs/api/conf.py
deleted file mode 100644
index 2199c2f..0000000
--- a/docs/api/conf.py
+++ /dev/null
@@ -1,331 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# mail documentation build configuration file, created by
-# sphinx-quickstart on Mon Aug 25 19:47:12 2014.
-#
-# This file is execfile()d with the current directory set to its
-# containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
-
-import sys
-import os
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
-
-# -- General configuration ------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be
-# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
-# ones.
-extensions = [
- 'sphinx.ext.autodoc',
- 'sphinx.ext.viewcode',
-]
-
-# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
-
-# The suffix of source filenames.
-source_suffix = '.rst'
-
-# The encoding of source files.
-#source_encoding = 'utf-8-sig'
-
-# The master toctree document.
-master_doc = 'index'
-
-# General information about the project.
-project = u'mail'
-copyright = u'2014, Author'
-
-# The version info for the project you're documenting, acts as replacement for
-# |version| and |release|, also used in various other places throughout the
-# built documents.
-#
-# The short X.Y version.
-version = ''
-# The full version, including alpha/beta/rc tags.
-release = ''
-
-# The language for content autogenerated by Sphinx. Refer to documentation
-# for a list of supported languages.
-#language = None
-
-# There are two options for replacing |today|: either, you set today to some
-# non-false value, then it is used:
-#today = ''
-# Else, today_fmt is used as the format for a strftime call.
-#today_fmt = '%B %d, %Y'
-
-# List of patterns, relative to source directory, that match files and
-# directories to ignore when looking for source files.
-exclude_patterns = ['_build']
-
-# The reST default role (used for this markup: `text`) to use for all
-# documents.
-#default_role = None
-
-# If true, '()' will be appended to :func: etc. cross-reference text.
-#add_function_parentheses = True
-
-# If true, the current module name will be prepended to all description
-# unit titles (such as .. function::).
-#add_module_names = True
-
-# If true, sectionauthor and moduleauthor directives will be shown in the
-# output. They are ignored by default.
-#show_authors = False
-
-# The name of the Pygments (syntax highlighting) style to use.
-pygments_style = 'sphinx'
-
-# A list of ignored prefixes for module index sorting.
-#modindex_common_prefix = []
-
-# If true, keep warnings as "system message" paragraphs in the built documents.
-#keep_warnings = False
-
-
-# -- Options for HTML output ----------------------------------------------
-
-# The theme to use for HTML and HTML Help pages. See the documentation for
-# a list of builtin themes.
-html_theme = 'default'
-
-# Theme options are theme-specific and customize the look and feel of a theme
-# further. For a list of options available for each theme, see the
-# documentation.
-#html_theme_options = {}
-
-# Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
-
-# The name for this set of Sphinx documents. If None, it defaults to
-# "<project> v<release> documentation".
-#html_title = None
-
-# A shorter title for the navigation bar. Default is the same as html_title.
-#html_short_title = None
-
-# The name of an image file (relative to this directory) to place at the top
-# of the sidebar.
-#html_logo = None
-
-# The name of an image file (within the static path) to use as favicon of the
-# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
-# pixels large.
-#html_favicon = None
-
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
-
-# Add any extra paths that contain custom files (such as robots.txt or
-# .htaccess) here, relative to this directory. These files are copied
-# directly to the root of the documentation.
-#html_extra_path = []
-
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-#html_last_updated_fmt = '%b %d, %Y'
-
-# If true, SmartyPants will be used to convert quotes and dashes to
-# typographically correct entities.
-#html_use_smartypants = True
-
-# Custom sidebar templates, maps document names to template names.
-#html_sidebars = {}
-
-# Additional templates that should be rendered to pages, maps page names to
-# template names.
-#html_additional_pages = {}
-
-# If false, no module index is generated.
-#html_domain_indices = True
-
-# If false, no index is generated.
-#html_use_index = True
-
-# If true, the index is split into individual pages for each letter.
-#html_split_index = False
-
-# If true, links to the reST sources are added to the pages.
-#html_show_sourcelink = True
-
-# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
-#html_show_sphinx = True
-
-# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
-#html_show_copyright = True
-
-# If true, an OpenSearch description file will be output, and all pages will
-# contain a <link> tag referring to it. The value of this option must be the
-# base URL from which the finished HTML is served.
-#html_use_opensearch = ''
-
-# This is the file name suffix for HTML files (e.g. ".xhtml").
-#html_file_suffix = None
-
-# Output file base name for HTML help builder.
-htmlhelp_basename = 'maildoc'
-
-
-# -- Options for LaTeX output ---------------------------------------------
-
-latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
-
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
-
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
-}
-
-# Grouping the document tree into LaTeX files. List of tuples
-# (source start file, target name, title,
-# author, documentclass [howto, manual, or own class]).
-latex_documents = [
- ('index', 'mail.tex', u'mail Documentation',
- u'Author', 'manual'),
-]
-
-# The name of an image file (relative to this directory) to place at the top of
-# the title page.
-#latex_logo = None
-
-# For "manual" documents, if this is true, then toplevel headings are parts,
-# not chapters.
-#latex_use_parts = False
-
-# If true, show page references after internal links.
-#latex_show_pagerefs = False
-
-# If true, show URL addresses after external links.
-#latex_show_urls = False
-
-# Documents to append as an appendix to all manuals.
-#latex_appendices = []
-
-# If false, no module index is generated.
-#latex_domain_indices = True
-
-
-# -- Options for manual page output ---------------------------------------
-
-# One entry per manual page. List of tuples
-# (source start file, name, description, authors, manual section).
-man_pages = [
- ('index', 'mail', u'mail Documentation',
- [u'Author'], 1)
-]
-
-# If true, show URL addresses after external links.
-#man_show_urls = False
-
-
-# -- Options for Texinfo output -------------------------------------------
-
-# Grouping the document tree into Texinfo files. List of tuples
-# (source start file, target name, title, author,
-# dir menu entry, description, category)
-texinfo_documents = [
- ('index', 'mail', u'mail Documentation',
- u'Author', 'mail', 'One line description of project.',
- 'Miscellaneous'),
-]
-
-# Documents to append as an appendix to all manuals.
-#texinfo_appendices = []
-
-# If false, no module index is generated.
-#texinfo_domain_indices = True
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#texinfo_show_urls = 'footnote'
-
-# If true, do not generate a @detailmenu in the "Top" node's menu.
-#texinfo_no_detailmenu = False
-
-
-# -- Options for Epub output ----------------------------------------------
-
-# Bibliographic Dublin Core info.
-epub_title = u'mail'
-epub_author = u'Author'
-epub_publisher = u'Author'
-epub_copyright = u'2014, Author'
-
-# The basename for the epub file. It defaults to the project name.
-#epub_basename = u'mail'
-
-# The HTML theme for the epub output. Since the default themes are not optimized
-# for small screen space, using the same theme for HTML and epub output is
-# usually not wise. This defaults to 'epub', a theme designed to save visual
-# space.
-#epub_theme = 'epub'
-
-# The language of the text. It defaults to the language option
-# or en if the language is not set.
-#epub_language = ''
-
-# The scheme of the identifier. Typical schemes are ISBN or URL.
-#epub_scheme = ''
-
-# The unique identifier of the text. This can be a ISBN number
-# or the project homepage.
-#epub_identifier = ''
-
-# A unique identification for the text.
-#epub_uid = ''
-
-# A tuple containing the cover image and cover page html template filenames.
-#epub_cover = ()
-
-# A sequence of (type, uri, title) tuples for the guide element of content.opf.
-#epub_guide = ()
-
-# HTML files that should be inserted before the pages created by sphinx.
-# The format is a list of tuples containing the path and title.
-#epub_pre_files = []
-
-# HTML files shat should be inserted after the pages created by sphinx.
-# The format is a list of tuples containing the path and title.
-#epub_post_files = []
-
-# A list of files that should not be packed into the epub file.
-epub_exclude_files = ['search.html']
-
-# The depth of the table of contents in toc.ncx.
-#epub_tocdepth = 3
-
-# Allow duplicate toc entries.
-#epub_tocdup = True
-
-# Choose between 'default' and 'includehidden'.
-#epub_tocscope = 'default'
-
-# Fix unsupported image types using the PIL.
-#epub_fix_images = False
-
-# Scale large images.
-#epub_max_image_width = 0
-
-# How to display URL addresses: 'footnote', 'no', or 'inline'.
-#epub_show_urls = 'inline'
-
-# If false, no index is generated.
-#epub_use_index = True
diff --git a/docs/api/index.rst b/docs/api/index.rst
deleted file mode 100644
index f5531df..0000000
--- a/docs/api/index.rst
+++ /dev/null
@@ -1,23 +0,0 @@
-.. mail documentation master file, created by
- sphinx-quickstart on Mon Aug 25 19:47:12 2014.
- You can adapt this file completely to your liking, but it should at least
- contain the root `toctree` directive.
-
-Welcome to mail's documentation!
-================================
-
-Contents:
-
-.. toctree::
- :maxdepth: 4
-
- mail
-
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
-
diff --git a/docs/api/leap.mail.adaptors.rst b/docs/api/leap.mail.adaptors.rst
new file mode 100644
index 0000000..472cade
--- /dev/null
+++ b/docs/api/leap.mail.adaptors.rst
@@ -0,0 +1,43 @@
+mail.adaptors package
+=====================
+
+.. automodule:: leap.mail.adaptors
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Subpackages
+-----------
+
+.. toctree::
+
+ leap.mail.adaptors.tests
+
+Submodules
+----------
+
+mail.adaptors.models module
+---------------------------
+
+.. automodule:: leap.mail.adaptors.models
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+mail.adaptors.soledad module
+----------------------------
+
+.. automodule:: leap.mail.adaptors.soledad
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+mail.adaptors.soledad_indexes module
+------------------------------------
+
+.. automodule:: leap.mail.adaptors.soledad_indexes
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
diff --git a/docs/api/leap.mail.adaptors.tests.rst b/docs/api/leap.mail.adaptors.tests.rst
new file mode 100644
index 0000000..2ae76e8
--- /dev/null
+++ b/docs/api/leap.mail.adaptors.tests.rst
@@ -0,0 +1,28 @@
+mail.adaptors.tests package
+===========================
+
+.. automodule:: leap.mail.adaptors.tests
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Submodules
+----------
+
+mail.adaptors.tests.test_models module
+--------------------------------------
+
+.. automodule:: leap.mail.adaptors.tests.test_models
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+mail.adaptors.tests.test_soledad_adaptor module
+-----------------------------------------------
+
+.. automodule:: leap.mail.adaptors.tests.test_soledad_adaptor
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
diff --git a/docs/api/leap.mail.imap.rst b/docs/api/leap.mail.imap.rst
new file mode 100644
index 0000000..bfaa3fd
--- /dev/null
+++ b/docs/api/leap.mail.imap.rst
@@ -0,0 +1,52 @@
+leap.mail.imap package
+=======================
+
+.. automodule:: leap.mail.imap
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Subpackages
+-----------
+
+.. toctree::
+
+ leap.mail.imap.service
+ leap.mail.imap.tests
+
+Submodules
+----------
+
+leap.mail.imap.account module
+------------------------------
+
+.. automodule:: leap.mail.imap.account
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+leap.mail.imap.mailbox module
+------------------------------
+
+.. automodule:: leap.mail.imap.mailbox
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+leap.mail.imap.messages module
+------------------------------
+
+.. automodule:: leap.mail.imap.messages
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+leap.mail.imap.server module
+-----------------------------
+
+.. automodule:: leap.mail.imap.server
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
diff --git a/docs/api/leap.mail.imap.service.rst b/docs/api/leap.mail.imap.service.rst
new file mode 100644
index 0000000..2f3ed4b
--- /dev/null
+++ b/docs/api/leap.mail.imap.service.rst
@@ -0,0 +1,9 @@
+leap.mail.imap.service package
+===============================
+
+.. automodule:: leap.mail.imap.service
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
diff --git a/docs/api/leap.mail.incoming.rst b/docs/api/leap.mail.incoming.rst
new file mode 100644
index 0000000..4bd1614
--- /dev/null
+++ b/docs/api/leap.mail.incoming.rst
@@ -0,0 +1,20 @@
+leap.mail.incoming package
+==========================
+
+.. automodule:: leap.mail.incoming
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Submodules
+----------
+
+leap.mail.incoming.service module
+----------------------------------
+
+.. automodule:: leap.mail.incoming.service
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
diff --git a/docs/api/leap.mail.outgoing.rst b/docs/api/leap.mail.outgoing.rst
new file mode 100644
index 0000000..af8c173
--- /dev/null
+++ b/docs/api/leap.mail.outgoing.rst
@@ -0,0 +1,21 @@
+leap.mail.outgoing package
+==========================
+
+.. automodule:: leap.mail.outgoing
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
+Submodules
+----------
+
+mail.outgoing.service module
+----------------------------
+
+.. automodule:: leap.mail.outgoing.service
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
diff --git a/docs/api/leap.mail.plugins.rst b/docs/api/leap.mail.plugins.rst
new file mode 100644
index 0000000..7a5d6b4
--- /dev/null
+++ b/docs/api/leap.mail.plugins.rst
@@ -0,0 +1,20 @@
+leap.mail.plugins package
+==========================
+
+.. automodule:: leap.mail.plugins
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Submodules
+----------
+
+leap.mail.plugins.soledad_sync_hooks module
+-------------------------------------------
+
+.. automodule:: leap.mail.plugins.soledad_sync_hooks
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
diff --git a/docs/api/leap.mail.rst b/docs/api/leap.mail.rst
new file mode 100644
index 0000000..686e648
--- /dev/null
+++ b/docs/api/leap.mail.rst
@@ -0,0 +1,105 @@
+leap.mail package
+=================
+
+.. automodule:: leap.mail
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Subpackages
+-----------
+
+.. toctree::
+
+ leap.mail.adaptors
+ leap.mail.imap
+ leap.mail.incoming
+ leap.mail.outgoing
+ leap.mail.plugins
+ leap.mail.smtp
+ leap.mail.tests
+
+Submodules
+----------
+
+leap.mail.constants module
+---------------------------
+
+.. automodule:: leap.mail.constants
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+leap.mail.decorators module
+---------------------------
+
+.. automodule:: leap.mail.decorators
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+leap.mail.interfaces module
+----------------------------
+
+.. automodule:: leap.mail.interfaces
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+leap.mail.load_tests module
+----------------------------
+
+.. automodule:: leap.mail.load_tests
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+leap.mail.mail module
+---------------------
+
+.. automodule:: leap.mail.mail
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+leap.mail.mailbox_indexer module
+---------------------------------
+
+.. automodule:: leap.mail.mailbox_indexer
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+leap.mail.size module
+----------------------
+
+.. automodule:: leap.mail.size
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+leap.mail.sync_hooks module
+----------------------------
+
+.. automodule:: leap.mail.sync_hooks
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+leap.mail.utils module
+-----------------------
+
+.. automodule:: leap.mail.utils
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+leap.mail.walk module
+----------------------
+
+.. automodule:: leap.mail.walk
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
diff --git a/docs/api/leap.mail.smtp.rst b/docs/api/leap.mail.smtp.rst
new file mode 100644
index 0000000..f35d3f9
--- /dev/null
+++ b/docs/api/leap.mail.smtp.rst
@@ -0,0 +1,19 @@
+leap.mail.smtp package
+=======================
+
+.. automodule:: leap.mail.smtp
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+Submodules
+----------
+
+leap.mail.smtp.gateway module
+-----------------------------
+
+.. automodule:: leap.mail.smtp.gateway
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
diff --git a/docs/api/mail.imap.rst b/docs/api/mail.imap.rst
deleted file mode 100644
index 051ded6..0000000
--- a/docs/api/mail.imap.rst
+++ /dev/null
@@ -1,118 +0,0 @@
-mail.imap package
-=================
-
-Subpackages
------------
-
-.. toctree::
-
- mail.imap.service
- mail.imap.tests
-
-Submodules
-----------
-
-mail.imap.account module
-------------------------
-
-.. automodule:: mail.imap.account
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.imap.fetch module
-----------------------
-
-.. automodule:: mail.imap.fetch
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.imap.fields module
------------------------
-
-.. automodule:: mail.imap.fields
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.imap.index module
-----------------------
-
-.. automodule:: mail.imap.index
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.imap.interfaces module
----------------------------
-
-.. automodule:: mail.imap.interfaces
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.imap.mailbox module
-------------------------
-
-.. automodule:: mail.imap.mailbox
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.imap.memorystore module
-----------------------------
-
-.. automodule:: mail.imap.memorystore
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.imap.messageparts module
------------------------------
-
-.. automodule:: mail.imap.messageparts
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.imap.messages module
--------------------------
-
-.. automodule:: mail.imap.messages
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.imap.parser module
------------------------
-
-.. automodule:: mail.imap.parser
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.imap.server module
------------------------
-
-.. automodule:: mail.imap.server
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.imap.soledadstore module
------------------------------
-
-.. automodule:: mail.imap.soledadstore
- :members:
- :undoc-members:
- :show-inheritance:
-
-
-Module contents
----------------
-
-.. automodule:: mail.imap
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/api/mail.imap.service.rst b/docs/api/mail.imap.service.rst
deleted file mode 100644
index c288813..0000000
--- a/docs/api/mail.imap.service.rst
+++ /dev/null
@@ -1,30 +0,0 @@
-mail.imap.service package
-=========================
-
-Submodules
-----------
-
-mail.imap.service.imap module
------------------------------
-
-.. automodule:: mail.imap.service.imap
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.imap.service.manhole module
---------------------------------
-
-.. automodule:: mail.imap.service.manhole
- :members:
- :undoc-members:
- :show-inheritance:
-
-
-Module contents
----------------
-
-.. automodule:: mail.imap.service
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/api/mail.imap.tests.rst b/docs/api/mail.imap.tests.rst
deleted file mode 100644
index b6717a3..0000000
--- a/docs/api/mail.imap.tests.rst
+++ /dev/null
@@ -1,38 +0,0 @@
-mail.imap.tests package
-=======================
-
-Submodules
-----------
-
-mail.imap.tests.imapclient module
----------------------------------
-
-.. automodule:: mail.imap.tests.imapclient
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.imap.tests.test_imap module
---------------------------------
-
-.. automodule:: mail.imap.tests.test_imap
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.imap.tests.walktree module
--------------------------------
-
-.. automodule:: mail.imap.tests.walktree
- :members:
- :undoc-members:
- :show-inheritance:
-
-
-Module contents
----------------
-
-.. automodule:: mail.imap.tests
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/api/mail.rst b/docs/api/mail.rst
deleted file mode 100644
index 2713207..0000000
--- a/docs/api/mail.rst
+++ /dev/null
@@ -1,70 +0,0 @@
-mail package
-============
-
-Subpackages
------------
-
-.. toctree::
-
- mail.imap
- mail.smtp
-
-Submodules
-----------
-
-mail.decorators module
-----------------------
-
-.. automodule:: mail.decorators
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.load_tests module
-----------------------
-
-.. automodule:: mail.load_tests
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.messageflow module
------------------------
-
-.. automodule:: mail.messageflow
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.size module
-----------------
-
-.. automodule:: mail.size
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.utils module
------------------
-
-.. automodule:: mail.utils
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.walk module
-----------------
-
-.. automodule:: mail.walk
- :members:
- :undoc-members:
- :show-inheritance:
-
-
-Module contents
----------------
-
-.. automodule:: mail
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/api/mail.smtp.rst b/docs/api/mail.smtp.rst
deleted file mode 100644
index da67279..0000000
--- a/docs/api/mail.smtp.rst
+++ /dev/null
@@ -1,37 +0,0 @@
-mail.smtp package
-=================
-
-Subpackages
------------
-
-.. toctree::
-
- mail.smtp.tests
-
-Submodules
-----------
-
-mail.smtp.gateway module
-------------------------
-
-.. automodule:: mail.smtp.gateway
- :members:
- :undoc-members:
- :show-inheritance:
-
-mail.smtp.rfc3156 module
-------------------------
-
-.. automodule:: mail.smtp.rfc3156
- :members:
- :undoc-members:
- :show-inheritance:
-
-
-Module contents
----------------
-
-.. automodule:: mail.smtp
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/api/mail.smtp.tests.rst b/docs/api/mail.smtp.tests.rst
deleted file mode 100644
index c313fb3..0000000
--- a/docs/api/mail.smtp.tests.rst
+++ /dev/null
@@ -1,22 +0,0 @@
-mail.smtp.tests package
-=======================
-
-Submodules
-----------
-
-mail.smtp.tests.test_gateway module
------------------------------------
-
-.. automodule:: mail.smtp.tests.test_gateway
- :members:
- :undoc-members:
- :show-inheritance:
-
-
-Module contents
----------------
-
-.. automodule:: mail.smtp.tests
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/api/make.bat b/docs/api/make.bat
deleted file mode 100644
index 63cd17f..0000000
--- a/docs/api/make.bat
+++ /dev/null
@@ -1,242 +0,0 @@
-@ECHO OFF
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=sphinx-build
-)
-set BUILDDIR=_build
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
-set I18NSPHINXOPTS=%SPHINXOPTS% .
-if NOT "%PAPER%" == "" (
- set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
- set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
-)
-
-if "%1" == "" goto help
-
-if "%1" == "help" (
- :help
- echo.Please use `make ^<target^>` where ^<target^> is one of
- echo. html to make standalone HTML files
- echo. dirhtml to make HTML files named index.html in directories
- echo. singlehtml to make a single large HTML file
- echo. pickle to make pickle files
- echo. json to make JSON files
- echo. htmlhelp to make HTML files and a HTML help project
- echo. qthelp to make HTML files and a qthelp project
- echo. devhelp to make HTML files and a Devhelp project
- echo. epub to make an epub
- echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
- echo. text to make text files
- echo. man to make manual pages
- echo. texinfo to make Texinfo files
- echo. gettext to make PO message catalogs
- echo. changes to make an overview over all changed/added/deprecated items
- echo. xml to make Docutils-native XML files
- echo. pseudoxml to make pseudoxml-XML files for display purposes
- echo. linkcheck to check all external links for integrity
- echo. doctest to run all doctests embedded in the documentation if enabled
- goto end
-)
-
-if "%1" == "clean" (
- for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
- del /q /s %BUILDDIR%\*
- goto end
-)
-
-
-%SPHINXBUILD% 2> nul
-if errorlevel 9009 (
- echo.
- echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
- echo.installed, then set the SPHINXBUILD environment variable to point
- echo.to the full path of the 'sphinx-build' executable. Alternatively you
- echo.may add the Sphinx directory to PATH.
- echo.
- echo.If you don't have Sphinx installed, grab it from
- echo.http://sphinx-doc.org/
- exit /b 1
-)
-
-if "%1" == "html" (
- %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/html.
- goto end
-)
-
-if "%1" == "dirhtml" (
- %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
- goto end
-)
-
-if "%1" == "singlehtml" (
- %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
- goto end
-)
-
-if "%1" == "pickle" (
- %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the pickle files.
- goto end
-)
-
-if "%1" == "json" (
- %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the JSON files.
- goto end
-)
-
-if "%1" == "htmlhelp" (
- %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run HTML Help Workshop with the ^
-.hhp project file in %BUILDDIR%/htmlhelp.
- goto end
-)
-
-if "%1" == "qthelp" (
- %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run "qcollectiongenerator" with the ^
-.qhcp project file in %BUILDDIR%/qthelp, like this:
- echo.^> qcollectiongenerator %BUILDDIR%\qthelp\mail.qhcp
- echo.To view the help file:
- echo.^> assistant -collectionFile %BUILDDIR%\qthelp\mail.ghc
- goto end
-)
-
-if "%1" == "devhelp" (
- %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished.
- goto end
-)
-
-if "%1" == "epub" (
- %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The epub file is in %BUILDDIR%/epub.
- goto end
-)
-
-if "%1" == "latex" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "latexpdf" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- cd %BUILDDIR%/latex
- make all-pdf
- cd %BUILDDIR%/..
- echo.
- echo.Build finished; the PDF files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "latexpdfja" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- cd %BUILDDIR%/latex
- make all-pdf-ja
- cd %BUILDDIR%/..
- echo.
- echo.Build finished; the PDF files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "text" (
- %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The text files are in %BUILDDIR%/text.
- goto end
-)
-
-if "%1" == "man" (
- %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The manual pages are in %BUILDDIR%/man.
- goto end
-)
-
-if "%1" == "texinfo" (
- %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
- goto end
-)
-
-if "%1" == "gettext" (
- %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
- goto end
-)
-
-if "%1" == "changes" (
- %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
- if errorlevel 1 exit /b 1
- echo.
- echo.The overview file is in %BUILDDIR%/changes.
- goto end
-)
-
-if "%1" == "linkcheck" (
- %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
- if errorlevel 1 exit /b 1
- echo.
- echo.Link check complete; look for any errors in the above output ^
-or in %BUILDDIR%/linkcheck/output.txt.
- goto end
-)
-
-if "%1" == "doctest" (
- %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
- if errorlevel 1 exit /b 1
- echo.
- echo.Testing of doctests in the sources finished, look at the ^
-results in %BUILDDIR%/doctest/output.txt.
- goto end
-)
-
-if "%1" == "xml" (
- %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The XML files are in %BUILDDIR%/xml.
- goto end
-)
-
-if "%1" == "pseudoxml" (
- %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
- goto end
-)
-
-:end
diff --git a/docs/conf.py b/docs/conf.py
index 95d919b..746f57c 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -18,9 +18,16 @@ import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-sys.path.insert(0, os.path.abspath('../src'))
-sys.path.insert(0, os.path.abspath('../src/leap'))
-sys.path.insert(0, os.path.abspath('../src/leap/mail'))
+#sys.path.insert(0, os.path.abspath('../src'))
+#sys.path.insert(0, os.path.abspath('../src/leap'))
+#sys.path.insert(0, os.path.abspath('../src/leap/mail'))
+#sys.path.insert(0, os.path.abspath('../../leap_common/src/leap/common'))
+#sys.path.insert(0, os.path.abspath('../../soledad/client/src/leap/soledad/client'))
+
+VENV_PATH = os.environ.get('VIRTUAL_ENV')
+if VENV_PATH:
+ sys.path.insert(0, os.path.abspath(VENV_PATH + 'lib/python2.7/site-packages'))
+
# -- General configuration ------------------------------------------------
@@ -57,7 +64,7 @@ copyright = u'2014-2015, The LEAP Encryption Access Project'
# built documents.
#
# The short X.Y version.
-version = '0.4.0alpha1'
+version = '0.4.0rc2'
# The full version, including alpha/beta/rc tags.
release = '0.4.0'
@@ -104,7 +111,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-html_theme = 'default'
+html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
diff --git a/docs/index.rst b/docs/index.rst
index 8bacc51..a2133f4 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -3,21 +3,50 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
-Welcome to leap.mail's documentation!
-=====================================
+leap.mail
+=========
-This is the documentation for the ``leap.mail`` module. It is a twisted package
-that exposes two services, ``smtp`` and ``imap``, that run local proxies and interact
-with a remote ``LEAP`` provider that offers *a soledad syncronization endpoint*
-and receive the outgoing email.
+*decentralized and secure mail delivery and synchronization*
+
+This is the documentation for the ``leap.mail`` module. It is a `twisted`_
+package that allows to receive, process, send and access existing messages using
+the `LEAP`_ platform.
+
+One way to use this library is to let it launch two standard mail services,
+``smtp`` and ``imap``, that run as local proxies and interact with a remote
+``LEAP`` provider that offers *a soledad syncronization endpoint* and receives
+the outgoing email. This is what `Bitmask`_ client does.
+
+From the release 0.4.0 on, it's also possible to use a protocol-agnostic email
+public API, so that third party mail clients can manipulate the data layer. This
+is what the awesome MUA in the `Pixelated`_ project is using.
+
+.. _`twisted`: https://twistedmatrix.com/trac/
+.. _`LEAP`: https://leap.se/en/docs
+.. _`Bitmask`: https://bitmask.net/en/features#email
+.. _`Pixelated`: https://pixelated-project.org/
+
+How does this all work?
+-----------------------
+
+All the underlying data storage and sync is handled by a library called
+`soledad`_, which handles encryption, storage and sync. Based on `u1db`_,
+documents are stored locally as local ``sqlcipher`` tables, and syncs against
+the soledad sync service in the provider.
+
+OpenPGP key generation and keyring management are handled by another leap
+python library: `keymanager`_.
See :ref:`the life cycle of a leap email <mail_journey>` for an overview of the life cycle
of an email through ``LEAP`` providers.
-``Soledad`` stores its documents as local ``sqlcipher`` tables, and syncs
-against the soledad sync service in the provider.
+.. _`Soledad`: https://leap.se/en/docs/design/soledad
+.. _`u1db`: https://en.wikipedia.org/wiki/U1DB
+.. _`keymanager`: https://github.com/leapcode/keymanager/
+Data model
+----------
.. TODO clear document types documentation.
The data model at the present moment consists of several *document types* that split email into
@@ -25,13 +54,8 @@ different documents that are stored in ``Soledad``. The idea behind this is to
keep clear the separation between *mutable* and *inmutable* parts, and still being able to
reconstruct arbitrarily nested email structures easily.
-In the coming releases we are going to be working towards the goal of exposing
-a protocol-agnostic email public API, so that third party mail clients can
-manipulate the data layer without having to resort to handling the sql tables or
-doing direct u1db calls. The code will be transitioning towards a LEAPMail
-public API that we can stabilize as soon as possible, and leaving the IMAP
-server as another code entity that uses this lower layer.
-
+Documentation index
+===================
..
.. Contents:
@@ -48,14 +72,18 @@ API documentation
-----------------
If you were looking for the documentation of the ``leap.mail`` module, you will
-find it here. Beware that the public API will still be unstable for the next
-development cycles.
+find it here.
+
+Of special interest is the `public mail api`_, which should remain relatively
+stable across the next few releases.
+
+.. _`public mail api`: api/mail.html#module-mail
+
.. toctree::
:maxdepth: 2
- api/mail
-
+ api/leap.mail
diff --git a/docs/intro.rst b/docs/intro.rst
deleted file mode 100644
index 6090a90..0000000
--- a/docs/intro.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-Introduction
-============
-
-leap.mail intro
diff --git a/docs/recreate_apidocs.sh b/docs/recreate_apidocs.sh
new file mode 100755
index 0000000..9a79d09
--- /dev/null
+++ b/docs/recreate_apidocs.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+# Watchout! this will need much manual touches
+# to the generated apidocs. Mainly: s/mail/leap.mail/g
+sphinx-apidoc -M -o api ../src/leap/mail
diff --git a/pkg/requirements-leap.pip b/pkg/requirements-leap.pip
index f50487e..feb9f37 100644
--- a/pkg/requirements-leap.pip
+++ b/pkg/requirements-leap.pip
@@ -1,3 +1,3 @@
-leap.common>=0.4.0
+leap.common>=0.4.3
leap.soledad.client>=0.7.0
leap.keymanager>=0.4.0
diff --git a/src/leap/mail/imap/mailbox.py b/src/leap/mail/imap/mailbox.py
index c52a2e3..c7accbb 100644
--- a/src/leap/mail/imap/mailbox.py
+++ b/src/leap/mail/imap/mailbox.py
@@ -215,11 +215,13 @@ class IMAPMailbox(object):
but in the future will be useful to get absolute UIDs from
message sequence numbers.
+
:param message: the message sequence number.
:type message: int
:rtype: int
:return: the UID of the message.
+
"""
# TODO support relative sequences. The (imap) message should
# receive a sequence number attribute: a deferred is not expected
@@ -558,7 +560,8 @@ class IMAPMailbox(object):
def _get_imap_msg(messages):
d_imapmsg = []
- for msg in messages:
+ # just in case we got bad data in here
+ for msg in filter(None, messages):
d_imapmsg.append(getimapmsg(msg))
return defer.gatherResults(d_imapmsg, consumeErrors=True)
diff --git a/src/leap/mail/imap/server.py b/src/leap/mail/imap/server.py
index 39f483f..8f14936 100644
--- a/src/leap/mail/imap/server.py
+++ b/src/leap/mail/imap/server.py
@@ -27,7 +27,7 @@ from twisted.mail import imap4
from twisted.python import log
from leap.common.check import leap_assert, leap_assert_type
-from leap.common.events import emit, catalog
+from leap.common.events import emit_async, catalog
from leap.soledad.client import Soledad
# imports for LITERAL+ patch
@@ -224,7 +224,7 @@ class LEAPIMAPServer(imap4.IMAP4Server):
# bad username, reject.
raise cred.error.UnauthorizedLogin()
# any dummy password is allowed so far. use realm instead!
- emit(catalog.IMAP_CLIENT_LOGIN, "1")
+ emit_async(catalog.IMAP_CLIENT_LOGIN, "1")
return imap4.IAccount, self.theAccount, lambda: None
def do_FETCH(self, tag, messages, query, uid=0):
diff --git a/src/leap/mail/imap/service/imap.py b/src/leap/mail/imap/service/imap.py
index c3ae59a..a50611b 100644
--- a/src/leap/mail/imap/service/imap.py
+++ b/src/leap/mail/imap/service/imap.py
@@ -28,7 +28,7 @@ from twisted.internet.protocol import ServerFactory
from twisted.mail import imap4
from twisted.python import log
-from leap.common.events import emit, catalog
+from leap.common.events import emit_async, catalog
from leap.common.check import leap_check
from leap.mail.imap.account import IMAPAccount
from leap.mail.imap.server import LEAPIMAPServer
@@ -158,8 +158,14 @@ def run_service(store, **kwargs):
factory = LeapIMAPFactory(uuid, userid, store)
try:
+ interface = "localhost"
+ # don't bind just to localhost if we are running on docker since we
+ # won't be able to access imap from the host
+ if os.environ.get("LEAP_DOCKERIZED"):
+ interface = ''
+
tport = reactor.listenTCP(port, factory,
- interface="localhost")
+ interface=interface)
except CannotListenError:
logger.error("IMAP Service failed to start: "
"cannot listen in port %s" % (port,))
@@ -178,10 +184,10 @@ def run_service(store, **kwargs):
reactor.listenTCP(manhole.MANHOLE_PORT, manhole_factory,
interface="127.0.0.1")
logger.debug("IMAP4 Server is RUNNING in port %s" % (port,))
- emit(catalog.IMAP_SERVICE_STARTED, str(port))
+ emit_async(catalog.IMAP_SERVICE_STARTED, str(port))
# FIXME -- change service signature
return tport, factory
# not ok, signal error.
- emit(catalog.IMAP_SERVICE_FAILED_TO_START, str(port))
+ emit_async(catalog.IMAP_SERVICE_FAILED_TO_START, str(port))
diff --git a/src/leap/mail/imap/tests/getmail b/src/leap/mail/imap/tests/getmail
index 0fb00d2..dd3fa0b 100755
--- a/src/leap/mail/imap/tests/getmail
+++ b/src/leap/mail/imap/tests/getmail
@@ -10,6 +10,7 @@ Simple IMAP4 client which displays the subjects of all messages in a
particular mailbox.
"""
+import os
import sys
from twisted.internet import protocol
@@ -20,6 +21,9 @@ from twisted.mail import imap4
from twisted.protocols import basic
from twisted.python import log
+# Global options stored here from main
+_opts = {}
+
class TrivialPrompter(basic.LineReceiver):
from os import linesep as delimiter
@@ -70,9 +74,7 @@ class SimpleIMAP4ClientFactory(protocol.ClientFactory):
"""
Initiate the protocol instance. Since we are building a simple IMAP
client, we don't bother checking what capabilities the server has. We
- just add all the authenticators twisted.mail has. Note: Gmail no
- longer uses any of the methods below, it's been using XOAUTH since
- 2010.
+ just add all the authenticators twisted.mail has.
"""
assert not self.usedUp
self.usedUp = True
@@ -159,14 +161,24 @@ def InsecureLogin(proto, username, password):
def cbMailboxList(result, proto):
"""
Callback invoked when a list of mailboxes has been retrieved.
+ If we have a selected mailbox in the global options, we directly pick it.
+ Otherwise, we offer a prompt to let user choose one.
"""
- result = [e[2] for e in result]
- s = '\n'.join(['%d. %s' % (n + 1, m) for (n, m) in zip(range(len(result)), result)])
+ all_mbox_list = [e[2] for e in result]
+ s = '\n'.join(['%d. %s' % (n + 1, m) for (n, m) in zip(range(len(all_mbox_list)), all_mbox_list)])
if not s:
return defer.fail(Exception("No mailboxes exist on server!"))
- return proto.prompt(s + "\nWhich mailbox? [1] "
- ).addCallback(cbPickMailbox, proto, result
- )
+
+ selected_mailbox = _opts.get('mailbox')
+
+ if not selected_mailbox:
+ return proto.prompt(s + "\nWhich mailbox? [1] "
+ ).addCallback(cbPickMailbox, proto, all_mbox_list
+ )
+ else:
+ mboxes_lower = map(lambda s: s.lower(), all_mbox_list)
+ index = mboxes_lower.index(selected_mailbox.lower()) + 1
+ return cbPickMailbox(index, proto, all_mbox_list)
def cbPickMailbox(result, proto, mboxes):
@@ -194,18 +206,34 @@ def cbExamineMbox(result, proto):
def cbFetch(result, proto):
"""
- Display headers.
+ Display a listing of the messages in the mailbox, based on the collected
+ headers.
"""
+ selected_subject = _opts.get('subject', None)
+ index = None
+
if result:
keys = result.keys()
keys.sort()
- for k in keys:
- proto.display('%s %s' % (k, result[k][0][2]))
+
+ if selected_subject:
+ for k in keys:
+ # remove 'Subject: ' preffix plus eol
+ subject = result[k][0][2][9:].rstrip('\r\n')
+ if subject.lower() == selected_subject.lower():
+ index = k
+ break
+ else:
+ for k in keys:
+ proto.display('%s %s' % (k, result[k][0][2]))
else:
print "Hey, an empty mailbox!"
- return proto.prompt("\nWhich message? [1] (Q quits) "
- ).addCallback(cbPickMessage, proto)
+ if not index:
+ return proto.prompt("\nWhich message? [1] (Q quits) "
+ ).addCallback(cbPickMessage, proto)
+ else:
+ return cbPickMessage(index, proto)
def cbPickMessage(result, proto):
@@ -247,16 +275,53 @@ def cbClose(result):
def main():
+ import argparse
+ import ConfigParser
import sys
+ from twisted.internet import reactor
+
+ description = (
+ 'Get messages from a LEAP IMAP Proxy.\nThis is a '
+ 'debugging tool, do not use this to retrieve any sensitive '
+ 'information, or we will send ninjas to your house!')
+ epilog = (
+ 'In case you want to automate the usage of this utility '
+ 'you can place your credentials in a file pointed by '
+ 'BITMASK_CREDENTIALS. You need to have a [Credentials] '
+ 'section, with username=<user@provider> and password fields')
+
+ parser = argparse.ArgumentParser(description=description, epilog=epilog)
+ credentials = os.environ.get('BITMASK_CREDENTIALS')
+
+ if credentials:
+ try:
+ config = ConfigParser.ConfigParser()
+ config.read(credentials)
+ username = config.get('Credentials', 'username')
+ password = config.get('Credentials', 'password')
+ except Exception, e:
+ print "Error reading credentials file: {0}".format(e)
+ sys.exit()
+ else:
+ parser.add_argument('username', type=str)
+ parser.add_argument('password', type=str)
- if len(sys.argv) != 3:
- print "Usage: getmail <user> <pass>"
- sys.exit()
+ parser.add_argument('--mailbox', dest='mailbox', default=None,
+ help='Which mailbox to retrieve. Empty for interactive prompt.')
+ parser.add_argument('--subject', dest='subject', default=None,
+ help='A subject for retrieve a mail that matches. Empty for interactive prompt.')
+
+ ns = parser.parse_args()
+
+ if not credentials:
+ username = ns.username
+ password = ns.password
+
+ _opts['mailbox'] = ns.mailbox
+ _opts['subject'] = ns.subject
hostname = "localhost"
port = "1984"
- username = sys.argv[1]
- password = sys.argv[2]
onConn = defer.Deferred(
).addCallback(cbServerGreeting, username, password
@@ -265,7 +330,6 @@ def main():
factory = SimpleIMAP4ClientFactory(username, onConn)
- from twisted.internet import reactor
if port == '993':
reactor.connectSSL(
hostname, int(port), factory, ssl.ClientContextFactory())
diff --git a/src/leap/mail/incoming/service.py b/src/leap/mail/incoming/service.py
index 2bc6751..d554c51 100644
--- a/src/leap/mail/incoming/service.py
+++ b/src/leap/mail/incoming/service.py
@@ -21,7 +21,6 @@ import copy
import logging
import shlex
import time
-import traceback
import warnings
from email.parser import Parser
@@ -33,18 +32,18 @@ from urlparse import urlparse
from twisted.application.service import Service
from twisted.python import log
+from twisted.python.failure import Failure
from twisted.internet import defer, reactor
from twisted.internet.task import LoopingCall
from twisted.internet.task import deferLater
-from u1db import errors as u1db_errors
-from leap.common.events import emit, catalog
+from leap.common.events import emit_async, catalog
from leap.common.check import leap_assert, leap_assert_type
from leap.common.mail import get_email_charset
from leap.keymanager import errors as keymanager_errors
from leap.keymanager.openpgp import OpenPGPKey
from leap.mail.adaptors import soledad_indexes as fields
-from leap.mail.utils import json_loads, empty, first
+from leap.mail.utils import json_loads, empty
from leap.soledad.client import Soledad
from leap.soledad.common.crypto import ENC_SCHEME_KEY, ENC_JSON_KEY
from leap.soledad.common.errors import InvalidAuthTokenError
@@ -90,6 +89,7 @@ class IncomingMail(Service):
CONTENT_KEY = "content"
LEAP_SIGNATURE_HEADER = 'X-Leap-Signature'
+ LEAP_ENCRYPTION_HEADER = 'X-Leap-Encryption'
"""
Header added to messages when they are decrypted by the fetcher,
which states the validity of an eventual signature that might be included
@@ -99,6 +99,8 @@ class IncomingMail(Service):
LEAP_SIGNATURE_INVALID = 'invalid'
LEAP_SIGNATURE_COULD_NOT_VERIFY = 'could not verify'
+ LEAP_ENCRYPTION_DECRYPTED = 'decrypted'
+
def __init__(self, keymanager, soledad, inbox, userid,
check_period=INCOMING_CHECK_PERIOD):
@@ -182,13 +184,21 @@ class IncomingMail(Service):
def startService(self):
"""
Starts a loop to fetch mail.
+
+ :returns: A Deferred whose callback will be invoked with
+ the LoopingCall instance when loop.stop is called, or
+ whose errback will be invoked when the function raises an
+ exception or returned a deferred that has its errback
+ invoked.
"""
Service.startService(self)
if self._loop is None:
self._loop = LoopingCall(self.fetch)
- return self._loop.start(self._check_period)
+ stop_deferred = self._loop.start(self._check_period)
+ return stop_deferred
else:
logger.warning("Tried to start an already running fetching loop.")
+ return defer.fail(Failure('Already running loop.'))
def stopService(self):
"""
@@ -228,7 +238,7 @@ class IncomingMail(Service):
except InvalidAuthTokenError:
# if the token is invalid, send an event so the GUI can
# disable mail and show an error message.
- emit(catalog.SOLEDAD_INVALID_AUTH_TOKEN)
+ emit_async(catalog.SOLEDAD_INVALID_AUTH_TOKEN)
def _signal_fetch_to_ui(self, doclist):
"""
@@ -244,16 +254,16 @@ class IncomingMail(Service):
num_mails = len(doclist) if doclist is not None else 0
if num_mails != 0:
log.msg("there are %s mails" % (num_mails,))
- emit(catalog.MAIL_FETCHED_INCOMING,
- str(num_mails), str(fetched_ts))
+ emit_async(catalog.MAIL_FETCHED_INCOMING,
+ str(num_mails), str(fetched_ts))
return doclist
def _signal_unread_to_ui(self, *args):
"""
Sends unread event to ui.
"""
- emit(catalog.MAIL_UNREAD_MESSAGES,
- str(self._inbox_collection.count_unseen()))
+ emit_async(catalog.MAIL_UNREAD_MESSAGES,
+ str(self._inbox_collection.count_unseen()))
# process incoming mail.
@@ -276,8 +286,8 @@ class IncomingMail(Service):
deferreds = []
for index, doc in enumerate(doclist):
logger.debug("processing doc %d of %d" % (index + 1, num_mails))
- emit(catalog.MAIL_MSG_PROCESSING,
- str(index), str(num_mails))
+ emit_async(catalog.MAIL_MSG_PROCESSING,
+ str(index), str(num_mails))
keys = doc.content.keys()
@@ -294,7 +304,7 @@ class IncomingMail(Service):
logger.debug("skipping msg with decrypting errors...")
elif self._is_msg(keys):
d = self._decrypt_doc(doc)
- d.addCallback(self._extract_keys)
+ d.addCallback(self._maybe_extract_keys)
d.addCallbacks(self._add_message_locally, self._errback)
deferreds.append(d)
d = defer.gatherResults(deferreds, consumeErrors=True)
@@ -326,7 +336,7 @@ class IncomingMail(Service):
decrdata = ""
success = False
- emit(catalog.MAIL_MSG_DECRYPTED, "1" if success else "0")
+ emit_async(catalog.MAIL_MSG_DECRYPTED, "1" if success else "0")
return self._process_decrypted_doc(doc, decrdata)
d = self._keymanager.decrypt(
@@ -461,6 +471,10 @@ class IncomingMail(Service):
d.addCallback(add_leap_header)
return d
+ def _add_decrypted_header(self, msg):
+ msg.add_header(self.LEAP_ENCRYPTION_HEADER,
+ self.LEAP_ENCRYPTION_DECRYPTED)
+
def _decrypt_multipart_encrypted_msg(self, msg, encoding, senderAddress):
"""
Decrypt a message with content-type 'multipart/encrypted'.
@@ -503,6 +517,7 @@ class IncomingMail(Service):
# all ok, replace payload by unencrypted payload
msg.set_payload(decrmsg.get_payload())
+ self._add_decrypted_header(msg)
return (msg, signkey)
d = self._keymanager.decrypt(
@@ -537,7 +552,9 @@ class IncomingMail(Service):
def decrypted_data(res):
decrdata, signkey = res
- return data.replace(pgp_message, decrdata), signkey
+ replaced_data = data.replace(pgp_message, decrdata)
+ self._add_decrypted_header(origmsg)
+ return replaced_data, signkey
def encode_and_return(res):
data, signkey = res
@@ -577,7 +594,8 @@ class IncomingMail(Service):
else:
return failure
- def _extract_keys(self, msgtuple):
+ @defer.inlineCallbacks
+ def _maybe_extract_keys(self, msgtuple):
"""
Retrieve attached keys to the mesage and parse message headers for an
*OpenPGP* header as described on the `IETF draft
@@ -604,20 +622,19 @@ class IncomingMail(Service):
msg = self._parser.parsestr(data)
_, fromAddress = parseaddr(msg['from'])
- header = msg.get(OpenPGP_HEADER, None)
- dh = defer.succeed(None)
- if header is not None:
- dh = self._extract_openpgp_header(header, fromAddress)
-
- da = defer.succeed(None)
+ valid_attachment = False
if msg.is_multipart():
- da = self._extract_attached_key(msg.get_payload(), fromAddress)
+ valid_attachment = yield self._maybe_extract_attached_key(
+ msg.get_payload(), fromAddress)
- d = defer.gatherResults([dh, da])
- d.addCallback(lambda _: msgtuple)
- return d
+ if not valid_attachment:
+ header = msg.get(OpenPGP_HEADER, None)
+ if header is not None:
+ yield self._maybe_extract_openpgp_header(header, fromAddress)
- def _extract_openpgp_header(self, header, address):
+ defer.returnValue(msgtuple)
+
+ def _maybe_extract_openpgp_header(self, header, address):
"""
Import keys from the OpenPGP header
@@ -662,7 +679,7 @@ class IncomingMail(Service):
% (header,))
return d
- def _extract_attached_key(self, attachments, address):
+ def _maybe_extract_attached_key(self, attachments, address):
"""
Import keys from the attachments
@@ -672,20 +689,33 @@ class IncomingMail(Service):
:type address: str
:return: A Deferred that will be fired when all the keys are stored
+ with a boolean: True if there was a valid key attached, or
+ False otherwise.
:rtype: Deferred
"""
MIME_KEY = "application/pgp-keys"
+ def log_key_added(ignored):
+ logger.debug('Added key found in attachment for %s' % address)
+ return True
+
+ def failed_put_key(failure):
+ logger.info("An error has ocurred adding attached key for %s: %s"
+ % (address, failure.getErrorMessage()))
+ return False
+
deferreds = []
for attachment in attachments:
if MIME_KEY == attachment.get_content_type():
- logger.debug("Add key from attachment")
d = self._keymanager.put_raw_key(
attachment.get_payload(),
OpenPGPKey,
address=address)
+ d.addCallbacks(log_key_added, failed_put_key)
deferreds.append(d)
- return defer.gatherResults(deferreds)
+ d = defer.gatherResults(deferreds)
+ d.addCallback(lambda result: any(result))
+ return d
def _add_message_locally(self, msgtuple):
"""
@@ -713,10 +743,10 @@ class IncomingMail(Service):
listener(result)
def signal_deleted(doc_id):
- emit(catalog.MAIL_MSG_DELETED_INCOMING)
+ emit_async(catalog.MAIL_MSG_DELETED_INCOMING)
return doc_id
- emit(catalog.MAIL_MSG_SAVED_LOCALLY)
+ emit_async(catalog.MAIL_MSG_SAVED_LOCALLY)
d = self._delete_incoming_message(doc)
d.addCallback(signal_deleted)
return d
diff --git a/src/leap/mail/incoming/tests/test_incoming_mail.py b/src/leap/mail/incoming/tests/test_incoming_mail.py
index f43f746..6880496 100644
--- a/src/leap/mail/incoming/tests/test_incoming_mail.py
+++ b/src/leap/mail/incoming/tests/test_incoming_mail.py
@@ -30,12 +30,13 @@ from email.parser import Parser
from mock import Mock
from twisted.internet import defer
+from leap.keymanager.errors import KeyAddressMismatch
from leap.keymanager.openpgp import OpenPGPKey
from leap.mail.adaptors import soledad_indexes as fields
from leap.mail.constants import INBOX_NAME
from leap.mail.imap.account import IMAPAccount
from leap.mail.incoming.service import IncomingMail
-from leap.mail.smtp.rfc3156 import MultipartEncrypted, PGPEncrypted
+from leap.mail.rfc3156 import MultipartEncrypted, PGPEncrypted
from leap.mail.tests import (
TestCaseWithKeyManager,
ADDRESS,
@@ -154,9 +155,6 @@ subject: independence of cyberspace
return d
def testExtractAttachedKey(self):
- """
- Test the OpenPGP header key extraction
- """
KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n..."
message = MIMEMultipart()
@@ -166,18 +164,101 @@ subject: independence of cyberspace
message.attach(key)
self.fetcher._keymanager.put_raw_key = Mock(
return_value=defer.succeed(None))
+
+ def put_raw_key_called(_):
+ self.fetcher._keymanager.put_raw_key.assert_called_once_with(
+ KEY, OpenPGPKey, address=ADDRESS_2)
+
+ d = self._do_fetch(message.as_string())
+ d.addCallback(put_raw_key_called)
+ return d
+
+ def testExtractInvalidAttachedKey(self):
+ KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n..."
+
+ message = MIMEMultipart()
+ message.add_header("from", ADDRESS_2)
+ key = MIMEApplication("", "pgp-keys")
+ key.set_payload(KEY)
+ message.attach(key)
+ self.fetcher._keymanager.put_raw_key = Mock(
+ return_value=defer.fail(KeyAddressMismatch()))
+
+ def put_raw_key_called(_):
+ self.fetcher._keymanager.put_raw_key.assert_called_once_with(
+ KEY, OpenPGPKey, address=ADDRESS_2)
+
+ d = self._do_fetch(message.as_string())
+ d.addCallback(put_raw_key_called)
+ return d
+
+ def testExtractAttachedKeyAndNotOpenPGPHeader(self):
+ KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n..."
+ KEYURL = "https://leap.se/key.txt"
+ OpenPGP = "id=12345678; url=\"%s\"; preference=signencrypt" % (KEYURL,)
+
+ message = MIMEMultipart()
+ message.add_header("from", ADDRESS_2)
+ message.add_header("OpenPGP", OpenPGP)
+ key = MIMEApplication("", "pgp-keys")
+ key.set_payload(KEY)
+ message.attach(key)
+
+ self.fetcher._keymanager.put_raw_key = Mock(
+ return_value=defer.succeed(None))
+ self.fetcher._keymanager.fetch_key = Mock()
+
+ def put_raw_key_called(_):
+ self.fetcher._keymanager.put_raw_key.assert_called_once_with(
+ KEY, OpenPGPKey, address=ADDRESS_2)
+ self.assertFalse(self.fetcher._keymanager.fetch_key.called)
+
+ d = self._do_fetch(message.as_string())
+ d.addCallback(put_raw_key_called)
+ return d
+
+ def testExtractOpenPGPHeaderIfInvalidAttachedKey(self):
+ KEY = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n..."
+ KEYURL = "https://leap.se/key.txt"
+ OpenPGP = "id=12345678; url=\"%s\"; preference=signencrypt" % (KEYURL,)
+
+ message = MIMEMultipart()
+ message.add_header("from", ADDRESS_2)
+ message.add_header("OpenPGP", OpenPGP)
+ key = MIMEApplication("", "pgp-keys")
+ key.set_payload(KEY)
+ message.attach(key)
+
+ self.fetcher._keymanager.put_raw_key = Mock(
+ return_value=defer.fail(KeyAddressMismatch()))
self.fetcher._keymanager.fetch_key = Mock()
def put_raw_key_called(_):
self.fetcher._keymanager.put_raw_key.assert_called_once_with(
KEY, OpenPGPKey, address=ADDRESS_2)
+ self.fetcher._keymanager.fetch_key.assert_called_once_with(
+ ADDRESS_2, KEYURL, OpenPGPKey)
d = self._do_fetch(message.as_string())
d.addCallback(put_raw_key_called)
return d
+ def testAddDecryptedHeader(self):
+ class DummyMsg():
+ def __init__(self):
+ self.headers = {}
+
+ def add_header(self, k, v):
+ self.headers[k] = v
+
+ msg = DummyMsg()
+ self.fetcher._add_decrypted_header(msg)
+
+ self.assertEquals(msg.headers['X-Leap-Encryption'], 'decrypted')
+
def testDecryptEmail(self):
self.fetcher._decryption_error = Mock()
+ self.fetcher._add_decrypted_header = Mock()
def create_encrypted_message(encstr):
message = Parser().parsestr(self.EMAIL)
@@ -198,9 +279,13 @@ subject: independence of cyberspace
return newmsg
def decryption_error_not_called(_):
- self.assertFalse(self.fetcher._decyption_error.called,
+ self.assertFalse(self.fetcher._decryption_error.called,
"There was some errors with decryption")
+ def add_decrypted_header_called(_):
+ self.assertTrue(self.fetcher._add_decrypted_header.called,
+ "There was some errors with decryption")
+
d = self._km.encrypt(
self.EMAIL,
ADDRESS, OpenPGPKey, sign=ADDRESS_2)
diff --git a/src/leap/mail/interfaces.py b/src/leap/mail/interfaces.py
index 899400f..10f5123 100644
--- a/src/leap/mail/interfaces.py
+++ b/src/leap/mail/interfaces.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# interfaces.py
-# Copyright (C) 2014 LEAP
+# Copyright (C) 2014,2015 LEAP
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -24,17 +24,71 @@ class IMessageWrapper(Interface):
"""
I know how to access the different parts into which a given message is
splitted into.
+
+ :ivar fdoc: dict with flag document.
+ :ivar hdoc: dict with flag document.
+ :ivar cdocs: dict with content-documents, one-indexed.
"""
fdoc = Attribute('A dictionaly-like containing the flags document '
'(mutable)')
- hdoc = Attribute('A dictionary-like containing the headers docuemnt '
+ hdoc = Attribute('A dictionary-like containing the headers document '
'(immutable)')
cdocs = Attribute('A dictionary with the content-docs, one-indexed')
+ def create(self, store, notify_just_mdoc=False, pending_inserts_dict={}):
+ """
+ Create the underlying wrapper.
+ """
+
+ def update(self, store):
+ """
+ Update the only mutable parts, which are within the flags document.
+ """
+
+ def delete(self, store):
+ """
+ Delete the parts for this wrapper that are not referenced from anywhere
+ else.
+ """
+
+ def copy(self, store, new_mbox_uuid):
+ """
+ Return a copy of this IMessageWrapper in a new mailbox.
+ """
+
+ def set_mbox_uuid(self, mbox_uuid):
+ """
+ Set the mailbox for this wrapper.
+ """
+
+ def set_flags(self, flags):
+ """
+ """
+
+ def set_tags(self, tags):
+ """
+ """
+
+ def set_date(self, date):
+ """
+ """
+
+ def get_subpart_dict(self, index):
+ """
+ :param index: the part to lookup, 1-indexed
+ """
+
+ def get_subpart_indexes(self):
+ """
+ """
+
+ def get_body(self, store):
+ """
+ """
+
-# TODO [ ] Catch up with the actual implementation!
-# Lot of stuff added there ...
+# TODO -- split into smaller interfaces? separate mailbox interface at least?
class IMailAdaptor(Interface):
"""
@@ -53,64 +107,109 @@ class IMailAdaptor(Interface):
:rtype: deferred
"""
- # TODO is staticmethod valid with an interface?
- # @staticmethod
def get_msg_from_string(self, MessageClass, raw_msg):
"""
- Return IMessageWrapper implementor from a raw mail string
+ Get an instance of a MessageClass initialized with a MessageWrapper
+ that contains all the parts obtained from parsing the raw string for
+ the message.
:param MessageClass: an implementor of IMessage
:type raw_msg: str
:rtype: implementor of leap.mail.IMessage
"""
- # TODO is staticmethod valid with an interface?
- # @staticmethod
- def get_msg_from_docs(self, MessageClass, msg_wrapper):
+ def get_msg_from_docs(self, MessageClass, mdoc, fdoc, hdoc, cdocs=None,
+ uid=None):
"""
- Return an IMessage implementor from its parts.
+ Get an instance of a MessageClass initialized with a MessageWrapper
+ that contains the passed part documents.
- :param MessageClass: an implementor of IMessage
- :param msg_wrapper: an implementor of IMessageWrapper
- :rtype: implementor of leap.mail.IMessage
+ This is not the recommended way of obtaining a message, unless you know
+ how to take care of ensuring the internal consistency between the part
+ documents, or unless you are glueing together the part documents that
+ have been previously generated by `get_msg_from_string`.
+ """
+
+ def get_flags_from_mdoc_id(self, store, mdoc_id):
+ """
+ """
+
+ def create_msg(self, store, msg):
+ """
+ :param store: an instance of soledad, or anything that behaves alike
+ :param msg: a Message object.
+
+ :return: a Deferred that is fired when all the underlying documents
+ have been created.
+ :rtype: defer.Deferred
+ """
+
+ def update_msg(self, store, msg):
"""
+ :param msg: a Message object.
+ :param store: an instance of soledad, or anything that behaves alike
+ :return: a Deferred that is fired when all the underlying documents
+ have been updated (actually, it's only the fdoc that's allowed
+ to update).
+ :rtype: defer.Deferred
+ """
+
+ def get_count_unseen(self, store, mbox_uuid):
+ """
+ Get the number of unseen messages for a given mailbox.
- # -------------------------------------------------------------------
- # XXX unsure about the following part yet ...........................
+ :param store: instance of Soledad.
+ :param mbox_uuid: the uuid for this mailbox.
+ :rtype: int
+ """
- # the idea behind these three methods is that the adaptor also offers a
- # fixed interface to create the documents the first time (using
- # soledad.create_docs or whatever method maps to it in a similar store, and
- # also allows to update flags and tags, hiding the actual implementation of
- # where the flags/tags live in behind the concrete MailWrapper in use
- # by this particular adaptor. In our impl it will be put_doc(fdoc) after
- # locking the getting + updating of that fdoc for atomicity.
+ def get_count_recent(self, store, mbox_uuid):
+ """
+ Get the number of recent messages for a given mailbox.
- # 'store' must be an instance of something that offers a minimal subset of
- # the document API that Soledad currently implements (create_doc, put_doc)
- # I *think* store should belong to Account/Collection and be passed as
- # param here instead of relying on it being an attribute of the instance.
+ :param store: instance of Soledad.
+ :param mbox_uuid: the uuid for this mailbox.
+ :rtype: int
+ """
- def create_msg_docs(self, store, msg_wrapper):
+ def get_mdoc_id_from_msgid(self, store, mbox_uuid, msgid):
"""
- :param store: The documents store
- :type store:
- :param msg_wrapper:
- :type msg_wrapper: IMessageWrapper implementor
+ Get the UID for a message with the passed msgid (the one in the headers
+ msg-id).
+ This is used by the MUA to retrieve the recently saved draft.
"""
- def update_msg_flags(self, store, msg_wrapper):
+ # mbox handling
+
+ def get_or_create_mbox(self, store, name):
"""
- :param store: The documents store
- :type store:
- :param msg_wrapper:
- :type msg_wrapper: IMessageWrapper implementor
+ Get the mailbox with the given name, or create one if it does not
+ exist.
+
+ :param store: instance of Soledad
+ :param name: the name of the mailbox
+ :type name: str
"""
- def update_msg_tags(self, store, msg_wrapper):
+ def update_mbox(self, store, mbox_wrapper):
"""
- :param store: The documents store
- :type store:
- :param msg_wrapper:
- :type msg_wrapper: IMessageWrapper implementor
+ Update the documents for a given mailbox.
+ :param mbox_wrapper: MailboxWrapper instance
+ :type mbox_wrapper: MailboxWrapper
+ :return: a Deferred that will be fired when the mailbox documents
+ have been updated.
+ :rtype: defer.Deferred
+ """
+
+ def delete_mbox(self, store, mbox_wrapper):
+ """
+ """
+
+ def get_all_mboxes(self, store):
+ """
+ Retrieve a list with wrappers for all the mailboxes.
+
+ :return: a deferred that will be fired with a list of all the
+ MailboxWrappers found.
+ :rtype: defer.Deferred
"""
diff --git a/src/leap/mail/mail.py b/src/leap/mail/mail.py
index 540a493..c0e16a6 100644
--- a/src/leap/mail/mail.py
+++ b/src/leap/mail/mail.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# mail.py
-# Copyright (C) 2014 LEAP
+# Copyright (C) 2014,2015 LEAP
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -15,7 +15,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-Generic Access to Mail objects: Public LEAP Mail API.
+Generic Access to Mail objects.
+
+This module holds the public LEAP Mail API, which should be viewed as the main
+entry point for message and account manipulation, in a protocol-agnostic way.
+
+In the future, pluggable transports will expose this generic API.
"""
import itertools
import uuid
@@ -28,7 +33,7 @@ from twisted.internet import defer
from twisted.python import log
from leap.common.check import leap_assert_type
-from leap.common.events import emit, catalog
+from leap.common.events import emit_async, catalog
from leap.common.mail import get_email_charset
from leap.mail.adaptors.soledad import SoledadMailAdaptor
@@ -148,6 +153,9 @@ class MessagePart(object):
# TODO This class should be better abstracted from the data model.
# TODO support arbitrarily nested multiparts (right now we only support
# the trivial case)
+ """
+ Represents a part of a multipart MIME Message.
+ """
def __init__(self, part_map, cdocs={}, nested=False):
"""
@@ -736,8 +744,7 @@ class MessageCollection(object):
:param unseen: number of unseen messages.
:type unseen: int
"""
- # TODO change name of the signal, independent from imap now.
- emit(catalog.MAIL_UNREAD_MESSAGES, str(unseen))
+ emit_async(catalog.MAIL_UNREAD_MESSAGES, str(unseen))
def copy_msg(self, msg, new_mbox_uuid):
"""
@@ -917,17 +924,19 @@ class Account(object):
adaptor_class = SoledadMailAdaptor
- # This is a mapping to collection instances so that we always
- # return a reference to them instead of creating new ones. However, being a
- # dictionary of weakrefs values, they automagically vanish from the dict
- # when no hard refs is left to them (so they can be garbage collected)
- # This is important because the different wrappers rely on several
- # kinds of deferredLocks that are kept as class or instance variables
- _collection_mapping = weakref.WeakValueDictionary()
-
def __init__(self, store, ready_cb=None):
self.store = store
self.adaptor = self.adaptor_class()
+
+ # this is a mapping to collection instances so that we always
+ # return a reference to them instead of creating new ones. however,
+ # being a dictionary of weakrefs values, they automagically vanish
+ # from the dict when no hard refs is left to them (so they can be
+ # garbage collected) this is important because the different wrappers
+ # rely on several kinds of deferredlocks that are kept as class or
+ # instance variables
+ self._collection_mapping = weakref.WeakValueDictionary()
+
self.mbox_indexer = MailboxIndexer(self.store)
# This flag is only used from the imap service for the moment.
diff --git a/src/leap/mail/mailbox_indexer.py b/src/leap/mail/mailbox_indexer.py
index 08e5f10..c49f808 100644
--- a/src/leap/mail/mailbox_indexer.py
+++ b/src/leap/mail/mailbox_indexer.py
@@ -15,6 +15,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
+.. :py:module::mailbox_indexer
+
Local tables to store the message Unique Identifiers for a given mailbox.
"""
import re
@@ -72,10 +74,11 @@ class MailboxIndexer(object):
These indexes are Message Attributes needed for the IMAP specification (rfc
3501), although they can be useful for other non-imap store
implementations.
+
"""
# The uids are expected to be 32-bits values, but the ROWIDs in sqlite
# are 64-bit values. I *don't* think it really matters for any
- # practical use, but it's good to remmeber we've got that difference going
+ # practical use, but it's good to remember we've got that difference going
# on.
store = None
diff --git a/src/leap/mail/outgoing/service.py b/src/leap/mail/outgoing/service.py
index 838a908..7cc5a24 100644
--- a/src/leap/mail/outgoing/service.py
+++ b/src/leap/mail/outgoing/service.py
@@ -31,17 +31,17 @@ from twisted.protocols.amp import ssl
from twisted.python import log
from leap.common.check import leap_assert_type, leap_assert
-from leap.common.events import emit, catalog
+from leap.common.events import emit_async, catalog
from leap.keymanager.openpgp import OpenPGPKey
from leap.keymanager.errors import KeyNotFound, KeyAddressMismatch
from leap.mail import __version__
from leap.mail.utils import validate_address
-from leap.mail.smtp.rfc3156 import MultipartEncrypted
-from leap.mail.smtp.rfc3156 import MultipartSigned
-from leap.mail.smtp.rfc3156 import encode_base64_rec
-from leap.mail.smtp.rfc3156 import RFC3156CompliantGenerator
-from leap.mail.smtp.rfc3156 import PGPSignature
-from leap.mail.smtp.rfc3156 import PGPEncrypted
+from leap.mail.rfc3156 import MultipartEncrypted
+from leap.mail.rfc3156 import MultipartSigned
+from leap.mail.rfc3156 import encode_base64_rec
+from leap.mail.rfc3156 import RFC3156CompliantGenerator
+from leap.mail.rfc3156 import PGPSignature
+from leap.mail.rfc3156 import PGPEncrypted
# TODO
# [ ] rename this module to something else, service should be the implementor
@@ -135,7 +135,7 @@ class OutgoingMail:
"""
dest_addrstr = smtp_sender_result[1][0][0]
log.msg('Message sent to %s' % dest_addrstr)
- emit(catalog.SMTP_SEND_MESSAGE_SUCCESS, dest_addrstr)
+ emit_async(catalog.SMTP_SEND_MESSAGE_SUCCESS, dest_addrstr)
def sendError(self, failure):
"""
@@ -145,7 +145,7 @@ class OutgoingMail:
:type e: anything
"""
# XXX: need to get the address from the exception to send signal
- # emit(catalog.SMTP_SEND_MESSAGE_ERROR, self._user.dest.addrstr)
+ # emit_async(catalog.SMTP_SEND_MESSAGE_ERROR, self._user.dest.addrstr)
err = failure.value
log.err(err)
raise err
@@ -178,7 +178,7 @@ class OutgoingMail:
requireAuthentication=False,
requireTransportSecurity=True)
factory.domain = __version__
- emit(catalog.SMTP_SEND_MESSAGE_START, recipient.dest.addrstr)
+ emit_async(catalog.SMTP_SEND_MESSAGE_START, recipient.dest.addrstr)
reactor.connectSSL(
self._host, self._port, factory,
contextFactory=SSLContextFactory(self._cert, self._key))
@@ -240,27 +240,27 @@ class OutgoingMail:
return d
def signal_encrypt_sign(newmsg):
- emit(catalog.SMTP_END_ENCRYPT_AND_SIGN,
- "%s,%s" % (self._from_address, to_address))
+ emit_async(catalog.SMTP_END_ENCRYPT_AND_SIGN,
+ "%s,%s" % (self._from_address, to_address))
return newmsg, recipient
def if_key_not_found_send_unencrypted(failure, message):
failure.trap(KeyNotFound, KeyAddressMismatch)
log.msg('Will send unencrypted message to %s.' % to_address)
- emit(catalog.SMTP_START_SIGN, self._from_address)
+ emit_async(catalog.SMTP_START_SIGN, self._from_address)
d = self._sign(message, from_address)
d.addCallback(signal_sign)
return d
def signal_sign(newmsg):
- emit(catalog.SMTP_END_SIGN, self._from_address)
+ emit_async(catalog.SMTP_END_SIGN, self._from_address)
return newmsg, recipient
log.msg("Will encrypt the message with %s and sign with %s."
% (to_address, from_address))
- emit(catalog.SMTP_START_ENCRYPT_AND_SIGN,
- "%s,%s" % (self._from_address, to_address))
+ emit_async(catalog.SMTP_START_ENCRYPT_AND_SIGN,
+ "%s,%s" % (self._from_address, to_address))
d = self._maybe_attach_key(origmsg, from_address, to_address)
d.addCallback(maybe_encrypt_and_sign)
return d
diff --git a/src/leap/mail/outgoing/tests/test_outgoing.py b/src/leap/mail/outgoing/tests/test_outgoing.py
index 2376da9..5518b33 100644
--- a/src/leap/mail/outgoing/tests/test_outgoing.py
+++ b/src/leap/mail/outgoing/tests/test_outgoing.py
@@ -30,7 +30,7 @@ from twisted.mail.smtp import User
from mock import Mock
from leap.mail.smtp.gateway import SMTPFactory
-from leap.mail.smtp.rfc3156 import RFC3156CompliantGenerator
+from leap.mail.rfc3156 import RFC3156CompliantGenerator
from leap.mail.outgoing.service import OutgoingMail
from leap.mail.tests import (
TestCaseWithKeyManager,
diff --git a/src/leap/mail/smtp/rfc3156.py b/src/leap/mail/rfc3156.py
index 7d7bc0f..7d7bc0f 100644
--- a/src/leap/mail/smtp/rfc3156.py
+++ b/src/leap/mail/rfc3156.py
diff --git a/src/leap/mail/smtp/__init__.py b/src/leap/mail/smtp/__init__.py
index 2ff14d7..7b62808 100644
--- a/src/leap/mail/smtp/__init__.py
+++ b/src/leap/mail/smtp/__init__.py
@@ -19,12 +19,13 @@
SMTP gateway helper function.
"""
import logging
+import os
from twisted.internet import reactor
from twisted.internet.error import CannotListenError
from leap.mail.outgoing.service import OutgoingMail
-from leap.common.events import emit, catalog
+from leap.common.events import emit_async, catalog
from leap.mail.smtp.gateway import SMTPFactory
logger = logging.getLogger(__name__)
@@ -64,13 +65,19 @@ def setup_smtp_gateway(port, userid, keymanager, smtp_host, smtp_port,
userid, keymanager, smtp_cert, smtp_key, smtp_host, smtp_port)
factory = SMTPFactory(userid, keymanager, encrypted_only, outgoing_mail)
try:
- tport = reactor.listenTCP(port, factory, interface="localhost")
- emit(catalog.SMTP_SERVICE_STARTED, str(port))
+ interface = "localhost"
+ # don't bind just to localhost if we are running on docker since we
+ # won't be able to access smtp from the host
+ if os.environ.get("LEAP_DOCKERIZED"):
+ interface = ''
+
+ tport = reactor.listenTCP(port, factory, interface=interface)
+ emit_async(catalog.SMTP_SERVICE_STARTED, str(port))
return factory, tport
except CannotListenError:
logger.error("STMP Service failed to start: "
"cannot listen in port %s" % port)
- emit(catalog.SMTP_SERVICE_FAILED_TO_START, str(port))
+ emit_async(catalog.SMTP_SERVICE_FAILED_TO_START, str(port))
except Exception as exc:
logger.error("Unhandled error while launching smtp gateway service")
logger.exception(exc)
diff --git a/src/leap/mail/smtp/gateway.py b/src/leap/mail/smtp/gateway.py
index 7dae907..45560bf 100644
--- a/src/leap/mail/smtp/gateway.py
+++ b/src/leap/mail/smtp/gateway.py
@@ -37,14 +37,11 @@ from twisted.python import log
from email.Header import Header
from leap.common.check import leap_assert_type
-from leap.common.events import emit, catalog
+from leap.common.events import emit_async, catalog
from leap.keymanager.openpgp import OpenPGPKey
from leap.keymanager.errors import KeyNotFound
from leap.mail.utils import validate_address
-
-from leap.mail.smtp.rfc3156 import (
- RFC3156CompliantGenerator,
-)
+from leap.mail.rfc3156 import RFC3156CompliantGenerator
# replace email generator with a RFC 3156 compliant one.
from email import generator
@@ -204,18 +201,19 @@ class SMTPDelivery(object):
# verify if recipient key is available in keyring
def found(_):
log.msg("Accepting mail for %s..." % user.dest.addrstr)
- emit(catalog.SMTP_RECIPIENT_ACCEPTED_ENCRYPTED, user.dest.addrstr)
+ emit_async(catalog.SMTP_RECIPIENT_ACCEPTED_ENCRYPTED,
+ user.dest.addrstr)
def not_found(failure):
failure.trap(KeyNotFound)
# if key was not found, check config to see if will send anyway
if self._encrypted_only:
- emit(catalog.SMTP_RECIPIENT_REJECTED, user.dest.addrstr)
+ emit_async(catalog.SMTP_RECIPIENT_REJECTED, user.dest.addrstr)
raise smtp.SMTPBadRcpt(user.dest.addrstr)
log.msg("Warning: will send an unencrypted message (because "
"encrypted_only' is set to False).")
- emit(
+ emit_async(
catalog.SMTP_RECIPIENT_ACCEPTED_UNENCRYPTED,
user.dest.addrstr)
@@ -309,7 +307,7 @@ class EncryptedMessage(object):
"""
log.msg("Connection lost unexpectedly!")
log.err()
- emit(catalog.SMTP_CONNECTION_LOST, self._user.dest.addrstr)
+ emit_async(catalog.SMTP_CONNECTION_LOST, self._user.dest.addrstr)
# unexpected loss of connection; don't save
self._lines = []
diff --git a/src/leap/mail/tests/__init__.py b/src/leap/mail/tests/__init__.py
index de0088f..71452d2 100644
--- a/src/leap/mail/tests/__init__.py
+++ b/src/leap/mail/tests/__init__.py
@@ -91,7 +91,7 @@ class TestCaseWithKeyManager(unittest.TestCase, BaseLeapTest):
nickserver_url = '' # the url of the nickserver
self._km = KeyManager(address, nickserver_url, self._soledad,
- ca_cert_path='', gpgbinary=self.GPG_BINARY_PATH)
+ gpgbinary=self.GPG_BINARY_PATH)
self._km._fetcher.put = Mock()
self._km._fetcher.get = Mock(return_value=Response())