diff options
author | Brad Warren <bmw@eff.org> | 2016-04-14 03:42:19 +0300 |
---|---|---|
committer | Brad Warren <bmw@eff.org> | 2016-04-14 03:42:19 +0300 |
commit | 214343ed6aa9db12524a45df34f5a26b73abed67 (patch) | |
tree | 8f3948fc4396ba9a95bdd78c5f119706bc54b17f /letshelp-certbot | |
parent | 0ce45a77f940013b1fc3f4e83b0e29102a98e69f (diff) |
rename letshelp-letsencrypt
Diffstat (limited to 'letshelp-certbot')
22 files changed, 1630 insertions, 0 deletions
diff --git a/letshelp-certbot/LICENSE.txt b/letshelp-certbot/LICENSE.txt new file mode 100644 index 000000000..981c46c9f --- /dev/null +++ b/letshelp-certbot/LICENSE.txt @@ -0,0 +1,190 @@ + Copyright 2015 Electronic Frontier Foundation and others + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/letshelp-certbot/MANIFEST.in b/letshelp-certbot/MANIFEST.in new file mode 100644 index 000000000..623392f28 --- /dev/null +++ b/letshelp-certbot/MANIFEST.in @@ -0,0 +1,4 @@ +include LICENSE.txt +include README.rst +recursive-include docs * +recursive-include letshelp_certbot/testdata * diff --git a/letshelp-certbot/README.rst b/letshelp-certbot/README.rst new file mode 100644 index 000000000..bbe2f2570 --- /dev/null +++ b/letshelp-certbot/README.rst @@ -0,0 +1 @@ +Let's help Certbot client diff --git a/letshelp-certbot/docs/.gitignore b/letshelp-certbot/docs/.gitignore new file mode 100644 index 000000000..ba65b13af --- /dev/null +++ b/letshelp-certbot/docs/.gitignore @@ -0,0 +1 @@ +/_build/ diff --git a/letshelp-certbot/docs/Makefile b/letshelp-certbot/docs/Makefile new file mode 100644 index 000000000..4b392ab8d --- /dev/null +++ b/letshelp-certbot/docs/Makefile @@ -0,0 +1,192 @@ +# 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 coverage 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 " applehelp to make an Apple Help Book" + @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)" + @echo " coverage to run coverage check of 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/letshelp-certbot.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/letshelp-certbot.qhc" + +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/letshelp-certbot" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/letshelp-certbot" + @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." + +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.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/letshelp-certbot/docs/_static/.gitignore b/letshelp-certbot/docs/_static/.gitignore new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/letshelp-certbot/docs/_static/.gitignore diff --git a/letshelp-certbot/docs/_templates/.gitignore b/letshelp-certbot/docs/_templates/.gitignore new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/letshelp-certbot/docs/_templates/.gitignore diff --git a/letshelp-certbot/docs/api.rst b/letshelp-certbot/docs/api.rst new file mode 100644 index 000000000..8668ec5d8 --- /dev/null +++ b/letshelp-certbot/docs/api.rst @@ -0,0 +1,8 @@ +================= +API Documentation +================= + +.. toctree:: + :glob: + + api/** diff --git a/letshelp-certbot/docs/api/index.rst b/letshelp-certbot/docs/api/index.rst new file mode 100644 index 000000000..5ced5f501 --- /dev/null +++ b/letshelp-certbot/docs/api/index.rst @@ -0,0 +1,11 @@ +:mod:`letshelp_certbot` +--------------------------- + +.. automodule:: letshelp_certbot + :members: + +:mod:`letshelp_certbot.apache` +================================== + +.. automodule:: letshelp_certbot.apache + :members: diff --git a/letshelp-certbot/docs/conf.py b/letshelp-certbot/docs/conf.py new file mode 100644 index 000000000..ba669113a --- /dev/null +++ b/letshelp-certbot/docs/conf.py @@ -0,0 +1,311 @@ +# -*- coding: utf-8 -*- +# +# letshelp-certbot documentation build configuration file, created by +# sphinx-quickstart on Sun Oct 18 13:40:19 2015. +# +# 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 +import shlex + + +here = os.path.abspath(os.path.dirname(__file__)) + +# 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(os.path.join(here, '..'))) + +# -- 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.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.viewcode', +] + +autodoc_member_order = 'bysource' +autodoc_default_flags = ['show-inheritance', 'private-members'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +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'letshelp-certbot' +copyright = u'2014-2015, Let\'s Encrypt Project' +author = u'Let\'s Encrypt Project' + +# 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 = '0' +# The full version, including alpha/beta/rc tags. +release = '0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = 'en' + +# 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 = 'py:obj' + +# 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 + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. + +# http://docs.readthedocs.org/en/latest/theme.html#how-do-i-use-this-locally-and-on-read-the-docs +# on_rtd is whether we are on readthedocs.org +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' +if not on_rtd: # only import and set the theme if we're building docs locally + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] +# otherwise, readthedocs.org uses their theme by default, so no need to specify it + +# 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 + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'letshelp-certbotdoc' + +# -- 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': '', + + # Latex figure (float) alignment + #'figure_align': 'htbp', +} + +# 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 = [ + (master_doc, 'letshelp-certbot.tex', u'letshelp-certbot Documentation', + u'Let\'s Encrypt Project', '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 = [ + (master_doc, 'letshelp-certbot', u'letshelp-certbot Documentation', + [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 = [ + (master_doc, 'letshelp-certbot', u'letshelp-certbot Documentation', + author, 'letshelp-certbot', '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 + + +intersphinx_mapping = { + 'python': ('https://docs.python.org/', None), + 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), + 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), +} diff --git a/letshelp-certbot/docs/index.rst b/letshelp-certbot/docs/index.rst new file mode 100644 index 000000000..678d9be2e --- /dev/null +++ b/letshelp-certbot/docs/index.rst @@ -0,0 +1,27 @@ +.. letshelp-certbot documentation master file, created by + sphinx-quickstart on Sun Oct 18 13:40:19 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to letshelp-certbot's documentation! +================================================ + +Contents: + +.. toctree:: + :maxdepth: 2 + + +.. toctree:: + :maxdepth: 1 + + api + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/letshelp-certbot/docs/make.bat b/letshelp-certbot/docs/make.bat new file mode 100644 index 000000000..0229b4f69 --- /dev/null +++ b/letshelp-certbot/docs/make.bat @@ -0,0 +1,263 @@ +@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 + echo. coverage to run coverage check of 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 +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 2> nul +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%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 +) + +:sphinx_ok + + +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\letshelp-certbot.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\letshelp-certbot.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 %~dp0 + 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 %~dp0 + 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" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.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/letshelp-certbot/letshelp_certbot/__init__.py b/letshelp-certbot/letshelp_certbot/__init__.py new file mode 100644 index 000000000..6882a19d4 --- /dev/null +++ b/letshelp-certbot/letshelp_certbot/__init__.py @@ -0,0 +1 @@ +"""Tools for submitting server configurations""" diff --git a/letshelp-certbot/letshelp_certbot/apache.py b/letshelp-certbot/letshelp_certbot/apache.py new file mode 100755 index 000000000..5752bdab0 --- /dev/null +++ b/letshelp-certbot/letshelp_certbot/apache.py @@ -0,0 +1,307 @@ +#!/usr/bin/env python +"""Certbot Apache configuration submission script""" + +from __future__ import print_function + +import argparse +import atexit +import contextlib +import os +import re +import shutil +import subprocess +import sys +import tarfile +import tempfile +import textwrap + + +_DESCRIPTION = """ +Let's Help is a simple script you can run to help out the Certbot +project. Since Certbot will support automatically configuring HTTPS on +many servers, we want to test this functionality on as many configurations as +possible. This script will create a sanitized copy of your Apache +configuration, notifying you of the files that have been selected. If (and only +if) you approve this selection, these files will be sent to the Certbot +developers. + +""" + + +_NO_APACHECTL = """ +Unable to find `apachectl` which is required for this script to work. If it is +installed, please run this script again with the --apache-ctl command line +argument and the path to the binary. + +""" + + +# Keywords likely to be found in filenames of sensitive files +_SENSITIVE_FILENAME_REGEX = re.compile(r"^(?!.*proxy_fdpass).*pass.*$|private|" + r"secret|^(?!.*certbot).*cert.*$|crt|" + r"key|rsa|dsa|pw|\.pem|\.der|\.p12|" + r"\.pfx|\.p7b") + + +def make_and_verify_selection(server_root, temp_dir): + """Copies server_root to temp_dir and verifies selection with the user + + :param str server_root: Path to the Apache server root + :param str temp_dir: Path to the temporary directory to copy files to + + """ + copied_files, copied_dirs = copy_config(server_root, temp_dir) + + print(textwrap.fill("A secure copy of the files that have been selected " + "for submission has been created under {0}. All " + "comments have been removed and the files are only " + "accessible by the current user. A list of the files " + "that have been included is shown below. Please make " + "sure that this selection does not contain private " + "keys, passwords, or any other sensitive " + "information.".format(temp_dir))) + print("\nFiles:") + for copied_file in copied_files: + print(copied_file) + print("Directories (including all contained files):") + for copied_dir in copied_dirs: + print(copied_dir) + + sys.stdout.write("\nIs it safe to submit these files? ") + while True: + ans = raw_input("(Y)es/(N)o: ").lower() + if ans.startswith("y"): + return + elif ans.startswith("n"): + sys.exit("Your files were not submitted") + + +def copy_config(server_root, temp_dir): + """Safely copies server_root to temp_dir and returns copied files + + :param str server_root: Absolute path to the Apache server root + :param str temp_dir: Path to the temporary directory to copy files to + + :returns: List of copied files and a list of leaf directories where + all contained files were copied + :rtype: `tuple` of `list` of `str` + + """ + copied_files, copied_dirs = [], [] + dir_len = len(os.path.dirname(server_root)) + + for config_path, config_dirs, config_files in os.walk(server_root): + temp_path = os.path.join(temp_dir, config_path[dir_len + 1:]) + os.mkdir(temp_path) + + copied_all = True + copied_files_in_current_dir = [] + for config_file in config_files: + config_file_path = os.path.join(config_path, config_file) + temp_file_path = os.path.join(temp_path, config_file) + if os.path.islink(config_file_path): + os.symlink(os.readlink(config_file_path), temp_file_path) + elif safe_config_file(config_file_path): + copy_file_without_comments(config_file_path, temp_file_path) + copied_files_in_current_dir.append(config_file_path) + else: + copied_all = False + + # If copied all files in leaf directory + if copied_all and not config_dirs: + copied_dirs.append(config_path) + else: + copied_files += copied_files_in_current_dir + + return copied_files, copied_dirs + + +def copy_file_without_comments(source, destination): + """Copies source to destination, removing comments + + :param str source: Path to the file to be copied + :param str destination: Path where source should be copied to + + """ + with open(source, "r") as infile: + with open(destination, "w") as outfile: + for line in infile: + if not (line.isspace() or line.lstrip().startswith("#")): + outfile.write(line) + + +def safe_config_file(config_file): + """Returns True if config_file can be safely copied + + :param str config_file: Path to an Apache configuration file + + :returns: True if config_file can be safely copied + :rtype: bool + + """ + config_file_lower = config_file.lower() + if _SENSITIVE_FILENAME_REGEX.search(config_file_lower): + return False + + proc = subprocess.Popen(["file", config_file], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + file_output, _ = proc.communicate() + + if "ASCII" in file_output: + possible_password_file = empty_or_all_comments = True + with open(config_file) as config_fd: + for line in config_fd: + if not (line.isspace() or line.lstrip().startswith("#")): + empty_or_all_comments = False + if line.startswith("-----BEGIN"): + return False + elif ":" not in line: + possible_password_file = False + # If file isn't empty or commented out and could be a password file, + # don't include it in selection. It is safe to include the file if + # it consists solely of comments because comments are removed before + # submission. + return empty_or_all_comments or not possible_password_file + + return False + + +def setup_tempdir(args): + """Creates a temporary directory and necessary files for config + + :param argparse.Namespace args: Parsed command line arguments + + :returns: Path to temporary directory + :rtype: str + + """ + tempdir = tempfile.mkdtemp() + + with open(os.path.join(tempdir, "config_file"), "w") as config_fd: + config_fd.write(args.config_file + "\n") + + proc = subprocess.Popen([args.apache_ctl, "-v"], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + with open(os.path.join(tempdir, "version"), "w") as version_fd: + version_fd.write(proc.communicate()[0]) + + proc = subprocess.Popen([args.apache_ctl, "-d", args.server_root, "-f", + args.config_file, "-M"], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + with open(os.path.join(tempdir, "modules"), "w") as modules_fd: + modules_fd.write(proc.communicate()[0]) + + proc = subprocess.Popen([args.apache_ctl, "-d", args.server_root, "-f", + args.config_file, "-t", "-D", "DUMP_VHOSTS"], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + with open(os.path.join(tempdir, "vhosts"), "w") as vhosts_fd: + vhosts_fd.write(proc.communicate()[0]) + + return tempdir + + +def verify_config(args): + """Verifies server_root and config_file specify a valid config + + :param argparse.Namespace args: Parsed command line arguments + + """ + with open(os.devnull, "w") as devnull: + try: + subprocess.check_call([args.apache_ctl, "-d", args.server_root, + "-f", args.config_file, "-t"], + stdout=devnull, stderr=subprocess.STDOUT) + except OSError: + sys.exit(_NO_APACHECTL) + except subprocess.CalledProcessError: + sys.exit("Syntax check from apachectl failed") + + +def locate_config(apache_ctl): + """Uses the apachectl binary to find configuration files + + :param str apache_ctl: Path to `apachectl` binary + + + :returns: Path to Apache server root and main configuration file + :rtype: `tuple` of `str` + + """ + try: + proc = subprocess.Popen([apache_ctl, "-V"], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output, _ = proc.communicate() + except OSError: + sys.exit(_NO_APACHECTL) + + server_root = config_file = "" + for line in output.splitlines(): + # Relevant output lines are of the form: -D DIRECTIVE="VALUE" + if "HTTPD_ROOT" in line: + server_root = line[line.find('"') + 1:-1] + elif "SERVER_CONFIG_FILE" in line: + config_file = line[line.find('"') + 1:-1] + + if not (server_root and config_file): + sys.exit("Unable to locate Apache configuration. Please run this " + "script again and specify --server-root and --config-file") + + return server_root, config_file + + +def get_args(): + """Parses command line arguments + + :returns: Parsed command line options + :rtype: argparse.Namespace + + """ + parser = argparse.ArgumentParser(description=_DESCRIPTION) + parser.add_argument("-c", "--apache-ctl", default="apachectl", + help="path to the `apachectl` binary") + parser.add_argument("-d", "--server-root", + help=("location of the root directory of your Apache " + "configuration")) + parser.add_argument("-f", "--config-file", + help=("location of your main Apache configuration " + "file relative to the server root")) + args = parser.parse_args() + + # args.server_root XOR args.config_file + if bool(args.server_root) != bool(args.config_file): + sys.exit("If either --server-root and --config-file are specified, " + "they both must be included") + elif args.server_root and args.config_file: + args.server_root = os.path.abspath(args.server_root) + args.config_file = os.path.abspath(args.config_file) + + if args.config_file.startswith(args.server_root): + args.config_file = args.config_file[len(args.server_root) + 1:] + else: + sys.exit("This script expects the Apache configuration file to be " + "inside the server root") + + return args + + +def main(): + """Main script execution""" + args = get_args() + if args.server_root is None: + args.server_root, args.config_file = locate_config(args.apache_ctl) + + verify_config(args) + tempdir = setup_tempdir(args) + atexit.register(lambda: shutil.rmtree(tempdir)) + make_and_verify_selection(args.server_root, tempdir) + + tarpath = os.path.join(tempdir, "config.tar.gz") + # contextlib.closing used for py26 support + with contextlib.closing(tarfile.open(tarpath, mode="w:gz")) as tar: + tar.add(tempdir, arcname=".") + + # TODO: Submit tarpath + + +if __name__ == "__main__": + main() # pragma: no cover diff --git a/letshelp-certbot/letshelp_certbot/apache_test.py b/letshelp-certbot/letshelp_certbot/apache_test.py new file mode 100644 index 000000000..0c1b5f2f6 --- /dev/null +++ b/letshelp-certbot/letshelp_certbot/apache_test.py @@ -0,0 +1,234 @@ +"""Tests for letshelp.letshelp_certbot_apache.py""" +import argparse +import functools +import os +import pkg_resources +import subprocess +import tarfile +import tempfile +import unittest + +import mock + +import letshelp_certbot.apache as letshelp_le_apache + + +_PARTIAL_CONF_PATH = os.path.join("mods-available", "ssl.load") +_PARTIAL_LINK_PATH = os.path.join("mods-enabled", "ssl.load") +_CONFIG_FILE = pkg_resources.resource_filename( + __name__, os.path.join("testdata", _PARTIAL_CONF_PATH)) +_PASSWD_FILE = pkg_resources.resource_filename( + __name__, os.path.join("testdata", "uncommonly_named_p4sswd")) +_KEY_FILE = pkg_resources.resource_filename( + __name__, os.path.join("testdata", "uncommonly_named_k3y")) +_SECRET_FILE = pkg_resources.resource_filename( + __name__, os.path.join("testdata", "super_secret_file.txt")) + + +_MODULE_NAME = "letshelp_certbot.apache" + + +_COMPILE_SETTINGS = """Server version: Apache/2.4.10 (Debian) +Server built: Mar 15 2015 09:51:43 +Server's Module Magic Number: 20120211:37 +Server loaded: APR 1.5.1, APR-UTIL 1.5.4 +Compiled using: APR 1.5.1, APR-UTIL 1.5.4 +Architecture: 64-bit +Server MPM: event + threaded: yes (fixed thread count) + forked: yes (variable process count) +Server compiled with.... + -D APR_HAS_SENDFILE + -D APR_HAS_MMAP + -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled) + -D APR_USE_SYSVSEM_SERIALIZE + -D APR_USE_PTHREAD_SERIALIZE + -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT + -D APR_HAS_OTHER_CHILD + -D AP_HAVE_RELIABLE_PIPED_LOGS + -D DYNAMIC_MODULE_LIMIT=256 + -D HTTPD_ROOT="/etc/apache2" + -D SUEXEC_BIN="/usr/lib/apache2/suexec" + -D DEFAULT_PIDLOG="/var/run/apache2.pid" + -D DEFAULT_SCOREBOARD="logs/apache_runtime_status" + -D DEFAULT_ERRORLOG="logs/error_log" + -D AP_TYPES_CONFIG_FILE="mime.types" + -D SERVER_CONFIG_FILE="apache2.conf" + +""" + + +class LetsHelpApacheTest(unittest.TestCase): + @mock.patch(_MODULE_NAME + ".copy_config") + def test_make_and_verify_selection(self, mock_copy_config): + mock_copy_config.return_value = (["apache2.conf"], ["apache2"]) + + with mock.patch("__builtin__.raw_input") as mock_input: + with mock.patch(_MODULE_NAME + ".sys.stdout"): + mock_input.side_effect = ["Yes", "No"] + letshelp_le_apache.make_and_verify_selection("root", "temp") + self.assertRaises( + SystemExit, letshelp_le_apache.make_and_verify_selection, + "server_root", "temp_dir") + + def test_copy_config(self): + tempdir = tempfile.mkdtemp() + server_root = pkg_resources.resource_filename(__name__, "testdata") + letshelp_le_apache.copy_config(server_root, tempdir) + + temp_testdata = os.path.join(tempdir, "testdata") + self.assertFalse(os.path.exists(os.path.join( + temp_testdata, os.path.basename(_PASSWD_FILE)))) + self.assertFalse(os.path.exists(os.path.join( + temp_testdata, os.path.basename(_KEY_FILE)))) + self.assertFalse(os.path.exists(os.path.join( + temp_testdata, os.path.basename(_SECRET_FILE)))) + self.assertTrue(os.path.exists(os.path.join( + temp_testdata, _PARTIAL_CONF_PATH))) + self.assertTrue(os.path.exists(os.path.join( + temp_testdata, _PARTIAL_LINK_PATH))) + + def test_copy_file_without_comments(self): + dest = tempfile.mkstemp()[1] + letshelp_le_apache.copy_file_without_comments(_PASSWD_FILE, dest) + + with open(_PASSWD_FILE) as original: + with open(dest) as copy: + for original_line, copied_line in zip(original, copy): + self.assertEqual(original_line, copied_line) + + @mock.patch(_MODULE_NAME + ".subprocess.Popen") + def test_safe_config_file(self, mock_popen): + mock_popen().communicate.return_value = ("PEM RSA private key", None) + self.assertFalse(letshelp_le_apache.safe_config_file("filename")) + + mock_popen().communicate.return_value = ("ASCII text", None) + self.assertFalse(letshelp_le_apache.safe_config_file(_PASSWD_FILE)) + self.assertFalse(letshelp_le_apache.safe_config_file(_KEY_FILE)) + self.assertFalse(letshelp_le_apache.safe_config_file(_SECRET_FILE)) + self.assertTrue(letshelp_le_apache.safe_config_file(_CONFIG_FILE)) + + @mock.patch(_MODULE_NAME + ".subprocess.Popen") + def test_tempdir(self, mock_popen): + mock_popen().communicate.side_effect = [ + ("version", None), ("modules", None), ("vhosts", None)] + args = _get_args() + + tempdir = letshelp_le_apache.setup_tempdir(args) + + with open(os.path.join(tempdir, "config_file")) as config_fd: + self.assertEqual(config_fd.read(), args.config_file + "\n") + + with open(os.path.join(tempdir, "version")) as version_fd: + self.assertEqual(version_fd.read(), "version") + + with open(os.path.join(tempdir, "modules")) as modules_fd: + self.assertEqual(modules_fd.read(), "modules") + + with open(os.path.join(tempdir, "vhosts")) as vhosts_fd: + self.assertEqual(vhosts_fd.read(), "vhosts") + + @mock.patch(_MODULE_NAME + ".subprocess.check_call") + def test_verify_config(self, mock_check_call): + args = _get_args() + mock_check_call.side_effect = [ + None, OSError, subprocess.CalledProcessError(1, "apachectl")] + + letshelp_le_apache.verify_config(args) + self.assertRaises(SystemExit, letshelp_le_apache.verify_config, args) + self.assertRaises(SystemExit, letshelp_le_apache.verify_config, args) + + @mock.patch(_MODULE_NAME + ".subprocess.Popen") + def test_locate_config(self, mock_popen): + mock_popen().communicate.side_effect = [ + OSError, ("bad_output", None), (_COMPILE_SETTINGS, None)] + + self.assertRaises( + SystemExit, letshelp_le_apache.locate_config, "ctl") + self.assertRaises( + SystemExit, letshelp_le_apache.locate_config, "ctl") + server_root, config_file = letshelp_le_apache.locate_config("ctl") + self.assertEqual(server_root, "/etc/apache2") + self.assertEqual(config_file, "apache2.conf") + + @mock.patch(_MODULE_NAME + ".argparse") + def test_get_args(self, mock_argparse): + argv = ["-d", "/etc/apache2"] + mock_argparse.ArgumentParser.return_value = _create_mock_parser(argv) + self.assertRaises(SystemExit, letshelp_le_apache.get_args) + + server_root = "/etc/apache2" + config_file = server_root + "/apache2.conf" + argv = ["-d", server_root, "-f", config_file] + mock_argparse.ArgumentParser.return_value = _create_mock_parser(argv) + args = letshelp_le_apache.get_args() + self.assertEqual(args.apache_ctl, "apachectl") + self.assertEqual(args.server_root, server_root) + self.assertEqual(args.config_file, os.path.basename(config_file)) + + server_root = "/etc/apache2" + config_file = "/etc/httpd/httpd.conf" + argv = ["-d", server_root, "-f", config_file] + mock_argparse.ArgumentParser.return_value = _create_mock_parser(argv) + self.assertRaises(SystemExit, letshelp_le_apache.get_args) + + def test_main_with_args(self): + with mock.patch(_MODULE_NAME + ".get_args"): + self._test_main_common() + + def test_main_without_args(self): + with mock.patch(_MODULE_NAME + ".get_args") as get_args: + args = _get_args() + server_root, config_file = args.server_root, args.config_file + args.server_root = args.config_file = None + get_args.return_value = args + with mock.patch(_MODULE_NAME + ".locate_config") as locate: + locate.return_value = (server_root, config_file) + self._test_main_common() + + def _test_main_common(self): + with mock.patch(_MODULE_NAME + ".verify_config"): + with mock.patch(_MODULE_NAME + ".setup_tempdir") as mock_setup: + tempdir_path = tempfile.mkdtemp() + mock_setup.return_value = tempdir_path + with mock.patch(_MODULE_NAME + ".make_and_verify_selection"): + testdir_basename = "test" + os.mkdir(os.path.join(tempdir_path, testdir_basename)) + + letshelp_le_apache.main() + + tar = tarfile.open(os.path.join( + tempdir_path, "config.tar.gz")) + + tempdir = tar.next() + self.assertTrue(tempdir.isdir()) + self.assertEqual(tempdir.name, ".") + + testdir = tar.next() + self.assertTrue(testdir.isdir()) + self.assertEqual(os.path.basename(testdir.name), + testdir_basename) + + self.assertEqual(tar.next(), None) + + +def _create_mock_parser(argv): + parser = argparse.ArgumentParser() + mock_parser = mock.MagicMock() + mock_parser.add_argument = parser.add_argument + mock_parser.parse_args = functools.partial(parser.parse_args, argv) + + return mock_parser + + +def _get_args(): + args = argparse.Namespace() + args.apache_ctl = "apache_ctl" + args.config_file = "config_file" + args.server_root = "server_root" + + return args + + +if __name__ == "__main__": + unittest.main() # pragma: no cover diff --git a/letshelp-certbot/letshelp_certbot/testdata/mods-available/ssl.load b/letshelp-certbot/letshelp_certbot/testdata/mods-available/ssl.load new file mode 100644 index 000000000..3d2336ae0 --- /dev/null +++ b/letshelp-certbot/letshelp_certbot/testdata/mods-available/ssl.load @@ -0,0 +1,2 @@ +# Depends: setenvif mime socache_shmcb +LoadModule ssl_module /usr/lib/apache2/modules/mod_ssl.so diff --git a/letshelp-certbot/letshelp_certbot/testdata/mods-enabled/ssl.load b/letshelp-certbot/letshelp_certbot/testdata/mods-enabled/ssl.load new file mode 120000 index 000000000..9d7972384 --- /dev/null +++ b/letshelp-certbot/letshelp_certbot/testdata/mods-enabled/ssl.load @@ -0,0 +1 @@ +../mods-available/ssl.load
\ No newline at end of file diff --git a/letshelp-certbot/letshelp_certbot/testdata/super_secret_file.txt b/letshelp-certbot/letshelp_certbot/testdata/super_secret_file.txt new file mode 100644 index 000000000..9f592eb7d --- /dev/null +++ b/letshelp-certbot/letshelp_certbot/testdata/super_secret_file.txt @@ -0,0 +1 @@ +hunter2 diff --git a/letshelp-certbot/letshelp_certbot/testdata/uncommonly_named_k3y b/letshelp-certbot/letshelp_certbot/testdata/uncommonly_named_k3y new file mode 100644 index 000000000..659274d1d --- /dev/null +++ b/letshelp-certbot/letshelp_certbot/testdata/uncommonly_named_k3y @@ -0,0 +1,6 @@ +-----BEGIN RSA PRIVATE KEY----- +MIGrAgEAAiEAm2Fylv+Uz7trgTW8EBHP3FQSMeZs2GNQ6VRo1sIVJEkCAwEAAQIh +AJT0BA/xD01dFCAXzSNyj9nfSZa3NpqzJZZn/eOm7vghAhEAzUVNZn4lLLBD1R6N +E8TKNQIRAMHHyn3O5JeY36lwKwkUlEUCEAliRauN0L0+QZuYjfJ9aJECEGx4dru3 +rTPCyighdqWNlHUCEQCiLjlwSRtWgmMBudCkVjzt +-----END RSA PRIVATE KEY----- diff --git a/letshelp-certbot/letshelp_certbot/testdata/uncommonly_named_p4sswd b/letshelp-certbot/letshelp_certbot/testdata/uncommonly_named_p4sswd new file mode 100644 index 000000000..3559c1d1f --- /dev/null +++ b/letshelp-certbot/letshelp_certbot/testdata/uncommonly_named_p4sswd @@ -0,0 +1 @@ +johntheripper:$apr1$fIGE9.JL$jTCwNWZy9Ak/yvOLuOyzQ1 diff --git a/letshelp-certbot/readthedocs.org.requirements.txt b/letshelp-certbot/readthedocs.org.requirements.txt new file mode 100644 index 000000000..7858b312f --- /dev/null +++ b/letshelp-certbot/readthedocs.org.requirements.txt @@ -0,0 +1,10 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e letshelp-certbot[docs] diff --git a/letshelp-certbot/setup.py b/letshelp-certbot/setup.py new file mode 100644 index 000000000..e625b288c --- /dev/null +++ b/letshelp-certbot/setup.py @@ -0,0 +1,59 @@ +import sys + +from setuptools import setup +from setuptools import find_packages + + +version = '0.6.0.dev0' + +install_requires = [ + 'setuptools', # pkg_resources +] +if sys.version_info < (2, 7): + install_requires.append('mock<1.1.0') +else: + install_requires.append('mock') + +docs_extras = [ + 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags + 'sphinx_rtd_theme', +] + +setup( + name='letshelp-certbot', + version=version, + description="Let's help Certbot client", + url='https://github.com/letsencrypt/letsencrypt', + author="Electronic Frontier Foundation", + author_email='client-dev@letsencrypt.org', + license='Apache License 2.0', + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Security', + 'Topic :: System :: Installation/Setup', + 'Topic :: System :: Networking', + 'Topic :: System :: Systems Administration', + 'Topic :: Utilities', + ], + + packages=find_packages(), + include_package_data=True, + install_requires=install_requires, + extras_require={ + 'docs': docs_extras, + }, + entry_points={ + 'console_scripts': [ + 'letshelp-certbot-apache = letshelp_certbot.apache:main', + ], + }, + test_suite='letshelp_certbot', +) |