summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/Makefile130
-rw-r--r--docs/_static/agogo.css452
-rw-r--r--docs/_static/basic.css532
-rw-r--r--docs/_static/default.css256
-rw-r--r--docs/_static/fonts/Museo_Slab_500.otfbin0 -> 62180 bytes
-rw-r--r--docs/_static/fonts/Museo_Slab_500italic.otfbin0 -> 66376 bytes
-rw-r--r--docs/_static/fonts/OFLGoudyStMTT-Italic.ttfbin0 -> 161252 bytes
-rw-r--r--docs/_static/fonts/OFLGoudyStMTT.ttfbin0 -> 173520 bytes
-rw-r--r--docs/_static/fonts/YanoneKaffeesatz-Bold.ttfbin0 -> 121352 bytes
-rw-r--r--docs/_static/fonts/YanoneKaffeesatz-Light.ttfbin0 -> 129136 bytes
-rw-r--r--docs/_static/fonts/YanoneKaffeesatz-Regular.ttfbin0 -> 128564 bytes
-rw-r--r--docs/_static/fonts/YanoneKaffeesatz-Thin.ttfbin0 -> 129060 bytes
-rw-r--r--docs/_static/haiku.css406
-rw-r--r--docs/_static/header.pngbin0 -> 16588 bytes
-rw-r--r--docs/_static/images/arch_layers.pngbin0 -> 27645 bytes
-rw-r--r--docs/_static/ir_black.css70
-rw-r--r--docs/_static/nature.css245
-rw-r--r--docs/_static/noise_dk.pngbin0 -> 22763 bytes
-rw-r--r--docs/_static/sphinxdoc.css339
-rw-r--r--docs/api/basexmpp.rst8
-rw-r--r--docs/api/clientxmpp.rst8
-rw-r--r--docs/api/componentxmpp.rst8
-rw-r--r--docs/api/exceptions.rst14
-rw-r--r--docs/api/xmlstream/filesocket.rst12
-rw-r--r--docs/api/xmlstream/handler.rst24
-rw-r--r--docs/api/xmlstream/jid.rst7
-rw-r--r--docs/api/xmlstream/matcher.rst41
-rw-r--r--docs/api/xmlstream/scheduler.rst11
-rw-r--r--docs/api/xmlstream/stanzabase.rst123
-rw-r--r--docs/api/xmlstream/tostring.rst46
-rw-r--r--docs/api/xmlstream/xmlstream.rst10
-rw-r--r--docs/architecture.rst177
-rw-r--r--docs/conf.py222
-rw-r--r--docs/create_plugin.rst679
-rw-r--r--docs/event_index.rst271
-rw-r--r--docs/features.rst2
-rw-r--r--docs/getting_started/component.rst75
-rw-r--r--docs/getting_started/echobot.rst390
-rw-r--r--docs/getting_started/iq.rst182
-rw-r--r--docs/getting_started/muc.rst2
-rw-r--r--docs/getting_started/presence.rst2
-rw-r--r--docs/getting_started/proxy.rst42
-rw-r--r--docs/getting_started/scheduler.rst2
-rw-r--r--docs/getting_started/sendlogout.rst94
-rw-r--r--docs/glossary.rst35
-rw-r--r--docs/guide_xep_0030.rst201
-rw-r--r--docs/handlersmatchers.rst4
-rw-r--r--docs/howto/stanzas.rst30
-rw-r--r--docs/index.rst179
-rw-r--r--docs/license.rst5
-rw-r--r--docs/make.bat170
-rw-r--r--docs/plugin_arch.rst2
-rw-r--r--docs/python-objects.invbin0 -> 105830 bytes
-rw-r--r--docs/sasl.rst2
-rw-r--r--docs/xeps.rst50
-rw-r--r--docs/xmpp_tdg.rst249
56 files changed, 5809 insertions, 0 deletions
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 00000000..a520f6a1
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,130 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
+
+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 " text to make text files"
+ @echo " man to make manual pages"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @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/SleekXMPP.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SleekXMPP.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/SleekXMPP"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SleekXMPP"
+ @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."
+
+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."
+
+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."
diff --git a/docs/_static/agogo.css b/docs/_static/agogo.css
new file mode 100644
index 00000000..8cdbf9c0
--- /dev/null
+++ b/docs/_static/agogo.css
@@ -0,0 +1,452 @@
+/*
+ * agogo.css_t
+ * ~~~~~~~~~~~
+ *
+ * Sphinx stylesheet -- agogo theme.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+* {
+ margin: 0px;
+ padding: 0px;
+}
+
+body {
+ font-family: "Verdana", Arial, sans-serif;
+ line-height: 1.4em;
+ color: black;
+ background-color: #eeeeec;
+}
+
+
+/* Page layout */
+
+div.header, div.content, div.footer {
+ width: 70em;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+div.header-wrapper {
+ background: url(bgtop.png) top left repeat-x;
+ border-bottom: 3px solid #2e3436;
+}
+
+
+/* Default body styles */
+a {
+ color: #ce5c00;
+}
+
+div.bodywrapper a, div.footer a {
+ text-decoration: underline;
+}
+
+.clearer {
+ clear: both;
+}
+
+.left {
+ float: left;
+}
+
+.right {
+ float: right;
+}
+
+.line-block {
+ display: block;
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+
+.line-block .line-block {
+ margin-top: 0;
+ margin-bottom: 0;
+ margin-left: 1.5em;
+}
+
+h1, h2, h3, h4 {
+ font-family: "Georgia", "Times New Roman", serif;
+ font-weight: normal;
+ color: #3465a4;
+ margin-bottom: .8em;
+}
+
+h1 {
+ color: #204a87;
+}
+
+h2 {
+ padding-bottom: .5em;
+ border-bottom: 1px solid #3465a4;
+}
+
+a.headerlink {
+ visibility: hidden;
+ color: #dddddd;
+ padding-left: .3em;
+}
+
+h1:hover > a.headerlink,
+h2:hover > a.headerlink,
+h3:hover > a.headerlink,
+h4:hover > a.headerlink,
+h5:hover > a.headerlink,
+h6:hover > a.headerlink,
+dt:hover > a.headerlink {
+ visibility: visible;
+}
+
+img {
+ border: 0;
+}
+
+div.admonition {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ padding: 2px 7px 1px 7px;
+ border-left: 0.2em solid black;
+}
+
+p.admonition-title {
+ margin: 0px 10px 5px 0px;
+ font-weight: bold;
+}
+
+dt:target, .highlighted {
+ background-color: #fbe54e;
+}
+
+/* Header */
+
+div.header {
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+
+div.header h1 {
+ font-family: "Georgia", "Times New Roman", serif;
+ font-weight: normal;
+ font-size: 180%;
+ letter-spacing: .08em;
+}
+
+div.header h1 a {
+ color: white;
+}
+
+div.header div.rel {
+ margin-top: 1em;
+}
+
+div.header div.rel a {
+ color: #fcaf3e;
+ letter-spacing: .1em;
+ text-transform: uppercase;
+}
+
+p.logo {
+ float: right;
+}
+
+img.logo {
+ border: 0;
+}
+
+
+/* Content */
+div.content-wrapper {
+ background-color: white;
+ padding-top: 20px;
+ padding-bottom: 20px;
+}
+
+div.document {
+ width: 50em;
+ float: left;
+}
+
+div.body {
+ padding-right: 2em;
+ text-align: justify;
+}
+
+div.document ul {
+ margin: 1.5em;
+ list-style-type: square;
+}
+
+div.document dd {
+ margin-left: 1.2em;
+ margin-top: .4em;
+ margin-bottom: 1em;
+}
+
+div.document .section {
+ margin-top: 1.7em;
+}
+div.document .section:first-child {
+ margin-top: 0px;
+}
+
+div.document div.highlight {
+ padding: 3px;
+ background-color: #eeeeec;
+ border-top: 2px solid #dddddd;
+ border-bottom: 2px solid #dddddd;
+ margin-top: .8em;
+ margin-bottom: .8em;
+}
+
+div.document h2 {
+ margin-top: .7em;
+}
+
+div.document p {
+ margin-bottom: .5em;
+}
+
+div.document li.toctree-l1 {
+ margin-bottom: 1em;
+}
+
+div.document .descname {
+ font-weight: bold;
+}
+
+div.document .docutils.literal {
+ background-color: #eeeeec;
+ padding: 1px;
+}
+
+div.document .docutils.xref.literal {
+ background-color: transparent;
+ padding: 0px;
+}
+
+div.document blockquote {
+ margin: 1em;
+}
+
+div.document ol {
+ margin: 1.5em;
+}
+
+
+/* Sidebar */
+
+div.sidebar {
+ width: 20em;
+ float: right;
+ font-size: .9em;
+}
+
+div.sidebar a, div.header a {
+ text-decoration: none;
+}
+
+div.sidebar a:hover, div.header a:hover {
+ text-decoration: underline;
+}
+
+div.sidebar h3 {
+ color: #2e3436;
+ text-transform: uppercase;
+ font-size: 130%;
+ letter-spacing: .1em;
+}
+
+div.sidebar ul {
+ list-style-type: none;
+}
+
+div.sidebar li.toctree-l1 a {
+ display: block;
+ padding: 1px;
+ border: 1px solid #dddddd;
+ background-color: #eeeeec;
+ margin-bottom: .4em;
+ padding-left: 3px;
+ color: #2e3436;
+}
+
+div.sidebar li.toctree-l2 a {
+ background-color: transparent;
+ border: none;
+ margin-left: 1em;
+ border-bottom: 1px solid #dddddd;
+}
+
+div.sidebar li.toctree-l3 a {
+ background-color: transparent;
+ border: none;
+ margin-left: 2em;
+ border-bottom: 1px solid #dddddd;
+}
+
+div.sidebar li.toctree-l2:last-child a {
+ border-bottom: none;
+}
+
+div.sidebar li.toctree-l1.current a {
+ border-right: 5px solid #fcaf3e;
+}
+
+div.sidebar li.toctree-l1.current li.toctree-l2 a {
+ border-right: none;
+}
+
+
+/* Footer */
+
+div.footer-wrapper {
+ background: url(bgfooter.png) top left repeat-x;
+ border-top: 4px solid #babdb6;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ min-height: 80px;
+}
+
+div.footer, div.footer a {
+ color: #888a85;
+}
+
+div.footer .right {
+ text-align: right;
+}
+
+div.footer .left {
+ text-transform: uppercase;
+}
+
+
+/* Styles copied from basic theme */
+
+img.align-left, .figure.align-left, object.align-left {
+ clear: left;
+ float: left;
+ margin-right: 1em;
+}
+
+img.align-right, .figure.align-right, object.align-right {
+ clear: right;
+ float: right;
+ margin-left: 1em;
+}
+
+img.align-center, .figure.align-center, object.align-center {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.align-left {
+ text-align: left;
+}
+
+.align-center {
+ clear: both;
+ text-align: center;
+}
+
+.align-right {
+ text-align: right;
+}
+
+/* -- search page ----------------------------------------------------------- */
+
+ul.search {
+ margin: 10px 0 0 20px;
+ padding: 0;
+}
+
+ul.search li {
+ padding: 5px 0 5px 20px;
+ background-image: url(file.png);
+ background-repeat: no-repeat;
+ background-position: 0 7px;
+}
+
+ul.search li a {
+ font-weight: bold;
+}
+
+ul.search li div.context {
+ color: #888;
+ margin: 2px 0 0 30px;
+ text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+ font-weight: bold;
+}
+
+/* -- index page ------------------------------------------------------------ */
+
+table.contentstable {
+ width: 90%;
+}
+
+table.contentstable p.biglink {
+ line-height: 150%;
+}
+
+a.biglink {
+ font-size: 1.3em;
+}
+
+span.linkdescr {
+ font-style: italic;
+ padding-top: 5px;
+ font-size: 90%;
+}
+
+/* -- general index --------------------------------------------------------- */
+
+table.indextable td {
+ text-align: left;
+ vertical-align: top;
+}
+
+table.indextable dl, table.indextable dd {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+table.indextable tr.pcap {
+ height: 10px;
+}
+
+table.indextable tr.cap {
+ margin-top: 10px;
+ background-color: #f2f2f2;
+}
+
+img.toggler {
+ margin-right: 3px;
+ margin-top: 3px;
+ cursor: pointer;
+}
+
+/* -- viewcode extension ---------------------------------------------------- */
+
+.viewcode-link {
+ float: right;
+}
+
+.viewcode-back {
+ float: right;
+ font-family:: "Verdana", Arial, sans-serif;
+}
+
+div.viewcode-block:target {
+ margin: -1px -3px;
+ padding: 0 3px;
+ background-color: #f4debf;
+ border-top: 1px solid #ac9;
+ border-bottom: 1px solid #ac9;
+} \ No newline at end of file
diff --git a/docs/_static/basic.css b/docs/_static/basic.css
new file mode 100644
index 00000000..888716a6
--- /dev/null
+++ b/docs/_static/basic.css
@@ -0,0 +1,532 @@
+/*
+ * basic.css
+ * ~~~~~~~~~
+ *
+ * Sphinx stylesheet -- basic theme.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+/* -- main layout ----------------------------------------------------------- */
+
+div.clearer {
+ clear: both;
+}
+
+/* -- relbar ---------------------------------------------------------------- */
+
+div.related {
+ width: 100%;
+ font-size: 90%;
+}
+
+div.related h3 {
+ display: none;
+}
+
+div.related ul {
+ margin: 0;
+ padding: 0 0 0 10px;
+ list-style: none;
+}
+
+div.related li {
+ display: inline;
+}
+
+div.related li.right {
+ float: right;
+ margin-right: 5px;
+}
+
+/* -- sidebar --------------------------------------------------------------- */
+
+div.sphinxsidebarwrapper {
+ padding: 10px 5px 0 10px;
+}
+
+div.sphinxsidebar {
+ float: left;
+ width: 230px;
+ margin-left: -100%;
+ font-size: 90%;
+}
+
+div.sphinxsidebar ul {
+ list-style: none;
+}
+
+div.sphinxsidebar ul ul,
+div.sphinxsidebar ul.want-points {
+ margin-left: 20px;
+ list-style: square;
+}
+
+div.sphinxsidebar ul ul {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+div.sphinxsidebar form {
+ margin-top: 10px;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #98dbcc;
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+img {
+ border: 0;
+}
+
+/* -- search page ----------------------------------------------------------- */
+
+ul.search {
+ margin: 10px 0 0 20px;
+ padding: 0;
+}
+
+ul.search li {
+ padding: 5px 0 5px 20px;
+ background-image: url(file.png);
+ background-repeat: no-repeat;
+ background-position: 0 7px;
+}
+
+ul.search li a {
+ font-weight: bold;
+}
+
+ul.search li div.context {
+ color: #888;
+ margin: 2px 0 0 30px;
+ text-align: left;
+}
+
+ul.keywordmatches li.goodmatch a {
+ font-weight: bold;
+}
+
+/* -- index page ------------------------------------------------------------ */
+
+table.contentstable {
+ width: 90%;
+}
+
+table.contentstable p.biglink {
+ line-height: 150%;
+}
+
+a.biglink {
+ font-size: 1.3em;
+}
+
+span.linkdescr {
+ font-style: italic;
+ padding-top: 5px;
+ font-size: 90%;
+}
+
+/* -- general index --------------------------------------------------------- */
+
+table.indextable {
+ width: 100%;
+}
+
+table.indextable td {
+ text-align: left;
+ vertical-align: top;
+}
+
+table.indextable dl, table.indextable dd {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+table.indextable tr.pcap {
+ height: 10px;
+}
+
+table.indextable tr.cap {
+ margin-top: 10px;
+ background-color: #f2f2f2;
+}
+
+img.toggler {
+ margin-right: 3px;
+ margin-top: 3px;
+ cursor: pointer;
+}
+
+div.modindex-jumpbox {
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+ margin: 1em 0 1em 0;
+ padding: 0.4em;
+}
+
+div.genindex-jumpbox {
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+ margin: 1em 0 1em 0;
+ padding: 0.4em;
+}
+
+/* -- general body styles --------------------------------------------------- */
+
+a.headerlink {
+ visibility: hidden;
+}
+
+h1:hover > a.headerlink,
+h2:hover > a.headerlink,
+h3:hover > a.headerlink,
+h4:hover > a.headerlink,
+h5:hover > a.headerlink,
+h6:hover > a.headerlink,
+dt:hover > a.headerlink {
+ visibility: visible;
+}
+
+div.body p.caption {
+ text-align: inherit;
+}
+
+div.body td {
+ text-align: left;
+}
+
+.field-list ul {
+ padding-left: 1em;
+}
+
+.first {
+ margin-top: 0 !important;
+}
+
+p.rubric {
+ margin-top: 30px;
+ font-weight: bold;
+}
+
+img.align-left, .figure.align-left, object.align-left {
+ clear: left;
+ float: left;
+ margin-right: 1em;
+}
+
+img.align-right, .figure.align-right, object.align-right {
+ clear: right;
+ float: right;
+ margin-left: 1em;
+}
+
+img.align-center, .figure.align-center, object.align-center {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.align-left {
+ text-align: left;
+}
+
+.align-center {
+ clear: both;
+ text-align: center;
+}
+
+.align-right {
+ text-align: right;
+}
+
+/* -- sidebars -------------------------------------------------------------- */
+
+div.sidebar {
+ margin: 0 0 0.5em 1em;
+ border: 1px solid #ddb;
+ padding: 7px 7px 0 7px;
+ background-color: #efefef;
+ width: 40%;
+ float: right;
+ -mox-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ border-radius: 4px;
+}
+
+p.sidebar-title {
+ font-weight: bold;
+ text-transform: uppercase;
+}
+
+/* -- topics ---------------------------------------------------------------- */
+
+div.topic {
+ border: 1px solid #ccc;
+ padding: 7px 7px 0 7px;
+ margin: 10px 0 10px 0;
+}
+
+p.topic-title {
+ font-size: 1.1em;
+ font-weight: bold;
+ margin-top: 10px;
+}
+
+/* -- admonitions ----------------------------------------------------------- */
+
+div.admonition {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ padding: 7px;
+}
+
+div.admonition dt {
+ font-weight: bold;
+}
+
+div.admonition dl {
+ margin-bottom: 0;
+}
+
+p.admonition-title {
+ margin: 0px 10px 5px 0px;
+ font-weight: bold;
+}
+
+div.body p.centered {
+ text-align: center;
+ margin-top: 25px;
+}
+
+/* -- tables ---------------------------------------------------------------- */
+
+table.docutils {
+ border: 0;
+ border-collapse: collapse;
+}
+
+table.docutils td, table.docutils th {
+ padding: 1px 8px 1px 5px;
+ border-top: 0;
+ border-left: 0;
+ border-right: 0;
+ border-bottom: 1px solid #aaa;
+}
+
+table.field-list td, table.field-list th {
+ border: 0 !important;
+}
+
+table.footnote td, table.footnote th {
+ border: 0 !important;
+}
+
+th {
+ text-align: left;
+ padding-right: 5px;
+}
+
+table.citation {
+ border-left: solid 1px gray;
+ margin-left: 1px;
+}
+
+table.citation td {
+ border-bottom: none;
+}
+
+/* -- other body styles ----------------------------------------------------- */
+
+ol.arabic {
+ list-style: decimal;
+}
+
+ol.loweralpha {
+ list-style: lower-alpha;
+}
+
+ol.upperalpha {
+ list-style: upper-alpha;
+}
+
+ol.lowerroman {
+ list-style: lower-roman;
+}
+
+ol.upperroman {
+ list-style: upper-roman;
+}
+
+dl {
+ margin-bottom: 15px;
+}
+
+dd p {
+ margin-top: 0px;
+}
+
+dd ul, dd table {
+ margin-bottom: 10px;
+}
+
+dd {
+ margin-top: 3px;
+ margin-bottom: 10px;
+ margin-left: 30px;
+}
+
+dt:target, .highlighted {
+}
+
+dl.glossary dt {
+ font-weight: bold;
+ font-size: 1.1em;
+}
+
+.field-list ul {
+ margin: 0;
+ padding-left: 1em;
+}
+
+.field-list p {
+ margin: 0;
+}
+
+.refcount {
+ color: #060;
+}
+
+.optional {
+ font-size: 1.3em;
+}
+
+.versionmodified {
+ font-style: italic;
+}
+
+.system-message {
+ background-color: #fda;
+ padding: 5px;
+ border: 3px solid red;
+}
+
+.footnote:target {
+ background-color: #ffa;
+}
+
+.line-block {
+ display: block;
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+
+.line-block .line-block {
+ margin-top: 0;
+ margin-bottom: 0;
+ margin-left: 1.5em;
+}
+
+.guilabel, .menuselection {
+ font-family: sans-serif;
+}
+
+.accelerator {
+ text-decoration: underline;
+}
+
+.classifier {
+ font-style: oblique;
+}
+
+/* -- code displays --------------------------------------------------------- */
+
+pre {
+ overflow: auto;
+ overflow-y: hidden; /* fixes display issues on Chrome browsers */
+}
+
+td.linenos pre {
+ padding: 5px 0px;
+ border: 0;
+ background-color: transparent;
+ color: #aaa;
+}
+
+table.highlighttable {
+ margin-left: 0.5em;
+}
+
+table.highlighttable td {
+ padding: 0 0.5em 0 0.5em;
+}
+
+tt.descname {
+ background-color: transparent;
+ font-weight: bold;
+ font-size: 1.2em;
+}
+
+tt.descclassname {
+ background-color: transparent;
+}
+
+tt.xref, a tt {
+ background-color: transparent;
+ font-weight: bold;
+}
+
+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
+ background-color: transparent;
+}
+
+.viewcode-link {
+ float: right;
+}
+
+.viewcode-back {
+ float: right;
+ font-family: sans-serif;
+}
+
+div.viewcode-block:target {
+ margin: -1px -10px;
+ padding: 0 10px;
+}
+
+/* -- math display ---------------------------------------------------------- */
+
+img.math {
+ vertical-align: middle;
+}
+
+div.body div.math p {
+ text-align: center;
+}
+
+span.eqno {
+ float: right;
+}
+
+/* -- printout stylesheet --------------------------------------------------- */
+
+@media print {
+ div.document,
+ div.documentwrapper,
+ div.bodywrapper {
+ margin: 0 !important;
+ width: 100%;
+ }
+
+ div.sphinxsidebar,
+ div.related,
+ div.footer,
+ #top-link {
+ display: none;
+ }
+}
+
diff --git a/docs/_static/default.css b/docs/_static/default.css
new file mode 100644
index 00000000..21f3f509
--- /dev/null
+++ b/docs/_static/default.css
@@ -0,0 +1,256 @@
+/*
+ * default.css_t
+ * ~~~~~~~~~~~~~
+ *
+ * Sphinx stylesheet -- default theme.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+@import url("basic.css");
+
+/* -- page layout ----------------------------------------------------------- */
+
+body {
+ font-family: sans-serif;
+ font-size: 100%;
+ background-color: #11303d;
+ color: #000;
+ margin: 0;
+ padding: 0;
+}
+
+div.document {
+ background-color: #1c4e63;
+}
+
+div.documentwrapper {
+ float: left;
+ width: 100%;
+}
+
+div.bodywrapper {
+ margin: 0 0 0 230px;
+}
+
+div.body {
+ background-color: #ffffff;
+ color: #000000;
+ padding: 0 20px 30px 20px;
+}
+
+div.footer {
+ color: #ffffff;
+ width: 100%;
+ padding: 9px 0 9px 0;
+ text-align: center;
+ font-size: 75%;
+}
+
+div.footer a {
+ color: #ffffff;
+ text-decoration: underline;
+}
+
+div.related {
+ background-color: #133f52;
+ line-height: 30px;
+ color: #ffffff;
+}
+
+div.related a {
+ color: #ffffff;
+}
+
+div.sphinxsidebar {
+}
+
+div.sphinxsidebar h3 {
+ font-family: 'Trebuchet MS', sans-serif;
+ color: #ffffff;
+ font-size: 1.4em;
+ font-weight: normal;
+ margin: 0;
+ padding: 0;
+}
+
+div.sphinxsidebar h3 a {
+ color: #ffffff;
+}
+
+div.sphinxsidebar h4 {
+ font-family: 'Trebuchet MS', sans-serif;
+ color: #ffffff;
+ font-size: 1.3em;
+ font-weight: normal;
+ margin: 5px 0 0 0;
+ padding: 0;
+}
+
+div.sphinxsidebar p {
+ color: #ffffff;
+}
+
+div.sphinxsidebar p.topless {
+ margin: 5px 10px 10px 10px;
+}
+
+div.sphinxsidebar ul {
+ margin: 10px;
+ padding: 0;
+ color: #ffffff;
+}
+
+div.sphinxsidebar a {
+ color: #98dbcc;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #98dbcc;
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+
+
+/* -- hyperlink styles ------------------------------------------------------ */
+
+a {
+ color: #355f7c;
+ text-decoration: none;
+}
+
+a:visited {
+ color: #355f7c;
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+
+
+/* -- body styles ----------------------------------------------------------- */
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+ font-family: 'Trebuchet MS', sans-serif;
+ background-color: #f2f2f2;
+ font-weight: normal;
+ color: #20435c;
+ border-bottom: 1px solid #ccc;
+ margin: 20px -20px 10px -20px;
+ padding: 3px 0 3px 10px;
+}
+
+div.body h1 { margin-top: 0; font-size: 200%; }
+div.body h2 { font-size: 160%; }
+div.body h3 { font-size: 140%; }
+div.body h4 { font-size: 120%; }
+div.body h5 { font-size: 110%; }
+div.body h6 { font-size: 100%; }
+
+a.headerlink {
+ color: #c60f0f;
+ font-size: 0.8em;
+ padding: 0 4px 0 4px;
+ text-decoration: none;
+}
+
+a.headerlink:hover {
+ background-color: #c60f0f;
+ color: white;
+}
+
+div.body p, div.body dd, div.body li {
+ text-align: justify;
+ line-height: 130%;
+}
+
+div.admonition p.admonition-title + p {
+ display: inline;
+}
+
+div.admonition p {
+ margin-bottom: 5px;
+}
+
+div.admonition pre {
+ margin-bottom: 5px;
+}
+
+div.admonition ul, div.admonition ol {
+ margin-bottom: 5px;
+}
+
+div.note {
+ background-color: #eee;
+ border: 1px solid #ccc;
+}
+
+div.seealso {
+ background-color: #ffc;
+ border: 1px solid #ff6;
+}
+
+div.topic {
+ background-color: #eee;
+}
+
+div.warning {
+ background-color: #ffe4e4;
+ border: 1px solid #f66;
+}
+
+p.admonition-title {
+ display: inline;
+}
+
+p.admonition-title:after {
+ content: ":";
+}
+
+pre {
+ padding: 5px;
+ background-color: #eeffcc;
+ color: #333333;
+ line-height: 120%;
+ border: 1px solid #ac9;
+ border-left: none;
+ border-right: none;
+}
+
+tt {
+ background-color: #ecf0f3;
+ padding: 0 1px 0 1px;
+ font-size: 0.95em;
+}
+
+th {
+ background-color: #ede;
+}
+
+.warning tt {
+ background: #efc2c2;
+}
+
+.note tt {
+ background: #d6d6d6;
+}
+
+.viewcode-back {
+ font-family: sans-serif;
+}
+
+div.viewcode-block:target {
+ background-color: #f4debf;
+ border-top: 1px solid #ac9;
+ border-bottom: 1px solid #ac9;
+} \ No newline at end of file
diff --git a/docs/_static/fonts/Museo_Slab_500.otf b/docs/_static/fonts/Museo_Slab_500.otf
new file mode 100644
index 00000000..84ceaca5
--- /dev/null
+++ b/docs/_static/fonts/Museo_Slab_500.otf
Binary files differ
diff --git a/docs/_static/fonts/Museo_Slab_500italic.otf b/docs/_static/fonts/Museo_Slab_500italic.otf
new file mode 100644
index 00000000..a8c055fd
--- /dev/null
+++ b/docs/_static/fonts/Museo_Slab_500italic.otf
Binary files differ
diff --git a/docs/_static/fonts/OFLGoudyStMTT-Italic.ttf b/docs/_static/fonts/OFLGoudyStMTT-Italic.ttf
new file mode 100644
index 00000000..91956cba
--- /dev/null
+++ b/docs/_static/fonts/OFLGoudyStMTT-Italic.ttf
Binary files differ
diff --git a/docs/_static/fonts/OFLGoudyStMTT.ttf b/docs/_static/fonts/OFLGoudyStMTT.ttf
new file mode 100644
index 00000000..dc900c29
--- /dev/null
+++ b/docs/_static/fonts/OFLGoudyStMTT.ttf
Binary files differ
diff --git a/docs/_static/fonts/YanoneKaffeesatz-Bold.ttf b/docs/_static/fonts/YanoneKaffeesatz-Bold.ttf
new file mode 100644
index 00000000..64e5d1ce
--- /dev/null
+++ b/docs/_static/fonts/YanoneKaffeesatz-Bold.ttf
Binary files differ
diff --git a/docs/_static/fonts/YanoneKaffeesatz-Light.ttf b/docs/_static/fonts/YanoneKaffeesatz-Light.ttf
new file mode 100644
index 00000000..15eccf5c
--- /dev/null
+++ b/docs/_static/fonts/YanoneKaffeesatz-Light.ttf
Binary files differ
diff --git a/docs/_static/fonts/YanoneKaffeesatz-Regular.ttf b/docs/_static/fonts/YanoneKaffeesatz-Regular.ttf
new file mode 100644
index 00000000..25cee817
--- /dev/null
+++ b/docs/_static/fonts/YanoneKaffeesatz-Regular.ttf
Binary files differ
diff --git a/docs/_static/fonts/YanoneKaffeesatz-Thin.ttf b/docs/_static/fonts/YanoneKaffeesatz-Thin.ttf
new file mode 100644
index 00000000..6e26d9e8
--- /dev/null
+++ b/docs/_static/fonts/YanoneKaffeesatz-Thin.ttf
Binary files differ
diff --git a/docs/_static/haiku.css b/docs/_static/haiku.css
new file mode 100644
index 00000000..615ed47b
--- /dev/null
+++ b/docs/_static/haiku.css
@@ -0,0 +1,406 @@
+/*
+ * haiku.css_t
+ * ~~~~~~~~~~~
+ *
+ * Sphinx stylesheet -- haiku theme.
+ *
+ * Adapted from http://haiku-os.org/docs/Haiku-doc.css.
+ * Original copyright message:
+ *
+ * Copyright 2008-2009, Haiku. All rights reserved.
+ * Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ * Francois Revol <revol@free.fr>
+ * Stephan Assmus <superstippi@gmx.de>
+ * Braden Ewing <brewin@gmail.com>
+ * Humdinger <humdingerb@gmail.com>
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+@import url("basic.css");
+
+
+@font-face {
+ font-family: "Museo Slab";
+ font-weight: normal;
+ font-style: normal;
+ src: local("Museo Slab"),
+ url("fonts/Museo_Slab_500.otf") format("opentype");
+}
+
+@font-face {
+ font-family: "Yanone Kaffeesatz";
+ font-weight: bold;
+ font-style: normal;
+ src: local("Yanone Kaffeesatz"),
+ url("fonts/YanoneKaffeesatz-Bold.ttf") format("truetype");
+}
+
+@font-face {
+ font-family: "Yanone Kaffeesatz";
+ font-weight: lighter;
+ font-style: normal;
+ src: local("Yanone Kaffeesatz"),
+ url("fonts/YanoneKaffeesatz-Regular.ttf") format("truetype");
+}
+
+html {
+ margin: 0px;
+ padding: 0px;
+ background: #FFF url(header.png) top left repeat-x;
+}
+
+body {
+ line-height: 1.5;
+ margin: auto;
+ padding: 0px;
+ font-family: "Helvetica Neueu", Helvetica, sans-serif;
+ min-width: 59em;
+ max-width: 70em;
+ color: #444;
+}
+
+div.footer {
+ padding: 8px;
+ font-size: 11px;
+ text-align: center;
+ letter-spacing: 0.5px;
+}
+
+/* link colors and text decoration */
+
+a:link {
+ font-weight: bold;
+ text-decoration: none;
+ color: #00ADEE;
+}
+
+a:visited {
+ font-weight: bold;
+ text-decoration: none;
+ color: #00ADEE;
+}
+
+a:hover, a:active {
+ text-decoration: underline;
+ color: #F46DBA;
+}
+
+/* Some headers act as anchors, don't give them a hover effect */
+
+h1 a:hover, a:active {
+ text-decoration: none;
+ color: #CFCFCF;
+}
+
+h2 a:hover, a:active {
+ text-decoration: none;
+ color: #CFCFCF;
+}
+
+h3 a:hover, a:active {
+ text-decoration: none;
+ color: #CFCFCF;
+}
+
+h4 a:hover, a:active {
+ text-decoration: none;
+ color: #CFCFCF;
+}
+
+a.headerlink {
+ color: #a7ce38;
+ padding-left: 5px;
+}
+
+a.headerlink:hover {
+ color: #a7ce38;
+}
+
+/* basic text elements */
+
+div.content {
+ margin-top: 20px;
+ margin-left: 40px;
+ margin-right: 40px;
+ margin-bottom: 50px;
+ font-size: 0.9em;
+}
+
+/* heading and navigation */
+
+div.header {
+ position: relative;
+ margin-top: 125px;
+ height: 85px;
+ padding: 0 40px;
+ font-family: "Yanone Kaffeesatz";
+}
+div.header h1 {
+ font-size: 2.6em;
+ font-weight: normal;
+ letter-spacing: 1px;
+ color: #CFCFCF;
+ border: 0;
+ margin: 0;
+ padding-top: 15px;
+ font-family: "Yanone Kaffeesatz";
+ text-shadow: 1px 1px 1px rgba(175, 175, 175, .8);
+ font-variant: small-caps;
+}
+div.header h1 a {
+ font-weight: normal;
+ color: #00ADEE;
+}
+div.header h2 {
+ font-size: 1.3em;
+ font-weight: normal;
+ letter-spacing: 1px;
+ text-transform: uppercase;
+ color: #aaa;
+ border: 0;
+ margin-top: -3px;
+ padding: 0;
+ font-family: "Yanone Kaffeesatz";
+}
+
+div.header img.rightlogo {
+ float: right;
+}
+
+
+div.title {
+ font-size: 1.3em;
+ font-weight: bold;
+ color: #CFCFCF;
+ border-bottom: dotted thin #e0e0e0;
+ margin-bottom: 25px;
+}
+div.topnav {
+ position: relative;
+ z-index: 0;
+}
+div.topnav p {
+ margin-top: 0;
+ margin-left: 40px;
+ margin-right: 40px;
+ margin-bottom: 0px;
+ text-align: right;
+ font-size: 0.8em;
+}
+div.bottomnav {
+ background: #eeeeee;
+}
+div.bottomnav p {
+ margin-right: 40px;
+ text-align: right;
+ font-size: 0.8em;
+}
+
+a.uplink {
+ font-weight: normal;
+}
+
+
+/* contents box */
+
+table.index {
+ margin: 0px 0px 30px 30px;
+ padding: 1px;
+ border-width: 1px;
+ border-style: dotted;
+ border-color: #e0e0e0;
+}
+table.index tr.heading {
+ background-color: #e0e0e0;
+ text-align: center;
+ font-weight: bold;
+ font-size: 1.1em;
+}
+table.index tr.index {
+ background-color: #eeeeee;
+}
+table.index td {
+ padding: 5px 20px;
+}
+
+table.index a:link, table.index a:visited {
+ font-weight: normal;
+ text-decoration: none;
+ color: #4A7389;
+}
+table.index a:hover, table.index a:active {
+ text-decoration: underline;
+ color: #ff4500;
+}
+
+
+/* Haiku User Guide styles and layout */
+
+/* Rounded corner boxes */
+/* Common declarations */
+div.admonition {
+ -webkit-border-radius: 10px;
+ -khtml-border-radius: 10px;
+ -moz-border-radius: 10px;
+ border-radius: 10px;
+ border-style: dotted;
+ border-width: thin;
+ border-color: #dcdcdc;
+ padding: 10px 15px 10px 15px;
+ margin-bottom: 15px;
+ margin-top: 15px;
+}
+div.note {
+ padding: 10px 15px 10px 15px;
+ background-color: #e4ffde;
+ /*background: #e4ffde url(alert_info_32.png) 15px 15px no-repeat;*/
+ min-height: 42px;
+}
+div.warning {
+ padding: 10px 15px 10px 15px;
+ background-color: #fffbc6;
+ /*background: #fffbc6 url(alert_warning_32.png) 15px 15px no-repeat;*/
+ min-height: 42px;
+}
+div.seealso {
+ background: #e4ffde;
+}
+
+/* More layout and styles */
+h1 {
+ font-size: 1.6em;
+ color: #aaa;
+ border-bottom: dotted thin #e0e0e0;
+ margin-top: 30px;
+ font-family: "Museo Slab";
+ text-shadow: 1px 1px 1px rgba(175, 175, 175, .25);
+}
+
+h2 {
+ font-size: 1.5em;
+ font-weight: normal;
+ color: #aaa;
+ border-bottom: dotted thin #e0e0e0;
+ margin-top: 30px;
+ font-family: "Museo Slab";
+ text-shadow: 1px 1px 1px rgba(175, 175, 175, .25);
+}
+
+h3 {
+ font-size: 1.4em;
+ font-weight: normal;
+ color: #aaa;
+ margin-top: 30px;
+ font-family: "Museo Slab";
+ text-shadow: 1px 1px 1px rgba(175, 175, 175, .25);
+}
+
+h4 {
+ font-size: 1.3em;
+ font-weight: normal;
+ color: #CFCFCF;
+ margin-top: 30px;
+}
+
+p {
+ text-align: justify;
+}
+
+p.last {
+ margin-bottom: 0;
+}
+
+ol {
+ padding-left: 20px;
+}
+
+ul {
+ padding-left: 5px;
+ margin-top: 3px;
+}
+
+li {
+ line-height: 1.3;
+}
+
+div.content ul > li {
+ -moz-background-clip:border;
+ -moz-background-inline-policy:continuous;
+ -moz-background-origin:padding;
+ background: transparent url(bullet_orange.png) no-repeat scroll left 0.45em;
+ list-style-image: none;
+ list-style-type: none;
+ padding: 0 0 0 1.666em;
+ margin-bottom: 3px;
+}
+
+td {
+ vertical-align: top;
+}
+
+tt {
+ background-color: #e2e2e2;
+ font-size: 1.0em;
+ font-family: monospace;
+}
+
+pre {
+ font-size: 1.1em;
+ margin: 0 0 12px 0;
+ padding: 0.8em;
+ background-image: url(noise_dk.png);
+ background-color: #222;
+}
+
+hr {
+ border-top: 1px solid #ccc;
+ border-bottom: 0;
+ border-right: 0;
+ border-left: 0;
+ margin-bottom: 10px;
+ margin-top: 20px;
+}
+
+/* printer only pretty stuff */
+@media print {
+ .noprint {
+ display: none;
+ }
+ /* for acronyms we want their definitions inlined at print time */
+ acronym[title]:after {
+ font-size: small;
+ content: " (" attr(title) ")";
+ font-style: italic;
+ }
+ /* and not have mozilla dotted underline */
+ acronym {
+ border: none;
+ }
+ div.topnav, div.bottomnav, div.header, table.index {
+ display: none;
+ }
+ div.content {
+ margin: 0px;
+ padding: 0px;
+ }
+ html {
+ background: #FFF;
+ }
+}
+
+.viewcode-back {
+ font-family: "DejaVu Sans", Arial, Helvetica, sans-serif;
+}
+
+div.viewcode-block:target {
+ border-top: 1px solid #ac9;
+ border-bottom: 1px solid #ac9;
+ margin: -1px -12px;
+ padding: 0 12px;
+}
+
diff --git a/docs/_static/header.png b/docs/_static/header.png
new file mode 100644
index 00000000..2aaa53a1
--- /dev/null
+++ b/docs/_static/header.png
Binary files differ
diff --git a/docs/_static/images/arch_layers.png b/docs/_static/images/arch_layers.png
new file mode 100644
index 00000000..1ee183bb
--- /dev/null
+++ b/docs/_static/images/arch_layers.png
Binary files differ
diff --git a/docs/_static/ir_black.css b/docs/_static/ir_black.css
new file mode 100644
index 00000000..f04bc738
--- /dev/null
+++ b/docs/_static/ir_black.css
@@ -0,0 +1,70 @@
+.highlight .hll { background-color: #ffffcc }
+.highlight { background: #000000; color: #f6f3e8; }
+.highlight .c { color: #7C7C7C; } /* Comment */
+.highlight .err { color: #f6f3e8; } /* Error */
+.highlight .g { color: #f6f3e8; } /* Generic */
+.highlight .k { color: #00ADEE; } /* Keyword */
+.highlight .l { color: #f6f3e8; } /* Literal */
+.highlight .n { color: #f6f3e8; } /* Name */
+.highlight .o { color: #f6f3e8; } /* Operator */
+.highlight .x { color: #f6f3e8; } /* Other */
+.highlight .p { color: #f6f3e8; } /* Punctuation */
+.highlight .cm { color: #7C7C7C; } /* Comment.Multiline */
+.highlight .cp { color: #96CBFE; } /* Comment.Preproc */
+.highlight .c1 { color: #7C7C7C; } /* Comment.Single */
+.highlight .cs { color: #7C7C7C; } /* Comment.Special */
+.highlight .gd { color: #f6f3e8; } /* Generic.Deleted */
+.highlight .ge { color: #f6f3e8; } /* Generic.Emph */
+.highlight .gr { color: #ffffff; background-color: #ff0000 } /* Generic.Error */
+.highlight .gh { color: #f6f3e8; font-weight: bold; } /* Generic.Heading */
+.highlight .gi { color: #f6f3e8; } /* Generic.Inserted */
+.highlight .go { color: #070707; } /* Generic.Output */
+.highlight .gp { color: #f6f3e8; } /* Generic.Prompt */
+.highlight .gs { color: #f6f3e8; } /* Generic.Strong */
+.highlight .gu { color: #f6f3e8; font-weight: bold; } /* Generic.Subheading */
+.highlight .gt { color: #ffffff; font-weight: bold; background-color: #FF6C60 } /* Generic.Traceback */
+.highlight .kc { color: #6699CC; } /* Keyword.Constant */
+.highlight .kd { color: #6699CC; } /* Keyword.Declaration */
+.highlight .kn { color: #6699CC; } /* Keyword.Namespace */
+.highlight .kp { color: #6699CC; } /* Keyword.Pseudo */
+.highlight .kr { color: #6699CC; } /* Keyword.Reserved */
+.highlight .kt { color: #FFFFB6; } /* Keyword.Type */
+.highlight .ld { color: #f6f3e8; } /* Literal.Date */
+.highlight .m { color: #FF73FD; } /* Literal.Number */
+.highlight .s { color: #F46DBA;/*#A8FF60;*/ } /* Literal.String */
+.highlight .na { color: #f6f3e8; } /* Name.Attribute */
+.highlight .nb { color: #f6f3e8; } /* Name.Builtin */
+.highlight .nc { color: #f6f3e8; } /* Name.Class */
+.highlight .no { color: #99CC99; } /* Name.Constant */
+.highlight .nd { color: #f6f3e8; } /* Name.Decorator */
+.highlight .ni { color: #E18964; } /* Name.Entity */
+.highlight .ne { color: #f6f3e8; } /* Name.Exception */
+.highlight .nf { color: #F64DBA; } /* Name.Function */
+.highlight .nl { color: #f6f3e8; } /* Name.Label */
+.highlight .nn { color: #f6f3e8; } /* Name.Namespace */
+.highlight .nx { color: #f6f3e8; } /* Name.Other */
+.highlight .py { color: #f6f3e8; } /* Name.Property */
+.highlight .nt { color: #00ADEE; } /* Name.Tag */
+.highlight .nv { color: #C6C5FE; } /* Name.Variable */
+.highlight .ow { color: #ffffff; } /* Operator.Word */
+.highlight .w { color: #f6f3e8; } /* Text.Whitespace */
+.highlight .mf { color: #FF73FD; } /* Literal.Number.Float */
+.highlight .mh { color: #FF73FD; } /* Literal.Number.Hex */
+.highlight .mi { color: #FF73FD; } /* Literal.Number.Integer */
+.highlight .mo { color: #FF73FD; } /* Literal.Number.Oct */
+.highlight .sb { color: #A8FF60; } /* Literal.String.Backtick */
+.highlight .sc { color: #A8FF60; } /* Literal.String.Char */
+.highlight .sd { color: #A8FF60; } /* Literal.String.Doc */
+.highlight .s2 { color: #A8FF60; } /* Literal.String.Double */
+.highlight .se { color: #A8FF60; } /* Literal.String.Escape */
+.highlight .sh { color: #A8FF60; } /* Literal.String.Heredoc */
+.highlight .si { color: #A8FF60; } /* Literal.String.Interpol */
+.highlight .sx { color: #A8FF60; } /* Literal.String.Other */
+.highlight .sr { color: #A8FF60; } /* Literal.String.Regex */
+.highlight .s1 { color: #A8FF60; } /* Literal.String.Single */
+.highlight .ss { color: #A8FF60; } /* Literal.String.Symbol */
+.highlight .bp { color: #f6f3e8; } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #C6C5FE; } /* Name.Variable.Class */
+.highlight .vg { color: #C6C5FE; } /* Name.Variable.Global */
+.highlight .vi { color: #C6C5FE; } /* Name.Variable.Instance */
+.highlight .il { color: #FF73FD; } /* Literal.Number.Integer.Long */
diff --git a/docs/_static/nature.css b/docs/_static/nature.css
new file mode 100644
index 00000000..52b328ea
--- /dev/null
+++ b/docs/_static/nature.css
@@ -0,0 +1,245 @@
+/*
+ * nature.css_t
+ * ~~~~~~~~~~~~
+ *
+ * Sphinx stylesheet -- nature theme.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+@import url("basic.css");
+
+/* -- page layout ----------------------------------------------------------- */
+
+body {
+ font-family: Arial, sans-serif;
+ font-size: 100%;
+ background-color: #111;
+ color: #555;
+ margin: 0;
+ padding: 0;
+}
+
+div.documentwrapper {
+ float: left;
+ width: 100%;
+}
+
+div.bodywrapper {
+ margin: 0 0 0 230px;
+}
+
+hr {
+ border: 1px solid #B1B4B6;
+}
+
+div.document {
+ background-color: #eee;
+}
+
+div.body {
+ background-color: #ffffff;
+ color: #3E4349;
+ padding: 0 30px 30px 30px;
+ font-size: 0.9em;
+}
+
+div.footer {
+ color: #555;
+ width: 100%;
+ padding: 13px 0;
+ text-align: center;
+ font-size: 75%;
+}
+
+div.footer a {
+ color: #444;
+ text-decoration: underline;
+}
+
+div.related {
+ background-color: #6BA81E;
+ line-height: 32px;
+ color: #fff;
+ text-shadow: 0px 1px 0 #444;
+ font-size: 0.9em;
+}
+
+div.related a {
+ color: #E2F3CC;
+}
+
+div.sphinxsidebar {
+ font-size: 0.75em;
+ line-height: 1.5em;
+}
+
+div.sphinxsidebarwrapper{
+ padding: 20px 0;
+}
+
+div.sphinxsidebar h3,
+div.sphinxsidebar h4 {
+ font-family: Arial, sans-serif;
+ color: #222;
+ font-size: 1.2em;
+ font-weight: normal;
+ margin: 0;
+ padding: 5px 10px;
+ background-color: #ddd;
+ text-shadow: 1px 1px 0 white
+}
+
+div.sphinxsidebar h4{
+ font-size: 1.1em;
+}
+
+div.sphinxsidebar h3 a {
+ color: #444;
+}
+
+
+div.sphinxsidebar p {
+ color: #888;
+ padding: 5px 20px;
+}
+
+div.sphinxsidebar p.topless {
+}
+
+div.sphinxsidebar ul {
+ margin: 10px 20px;
+ padding: 0;
+ color: #000;
+}
+
+div.sphinxsidebar a {
+ color: #444;
+}
+
+div.sphinxsidebar input {
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 1em;
+}
+
+div.sphinxsidebar input[type=text]{
+ margin-left: 20px;
+}
+
+/* -- body styles ----------------------------------------------------------- */
+
+a {
+ color: #005B81;
+ text-decoration: none;
+}
+
+a:hover {
+ color: #E32E00;
+ text-decoration: underline;
+}
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+ font-family: Arial, sans-serif;
+ background-color: #BED4EB;
+ font-weight: normal;
+ color: #212224;
+ margin: 30px 0px 10px 0px;
+ padding: 5px 0 5px 10px;
+ text-shadow: 0px 1px 0 white
+}
+
+div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
+div.body h2 { font-size: 150%; background-color: #C8D5E3; }
+div.body h3 { font-size: 120%; background-color: #D8DEE3; }
+div.body h4 { font-size: 110%; background-color: #D8DEE3; }
+div.body h5 { font-size: 100%; background-color: #D8DEE3; }
+div.body h6 { font-size: 100%; background-color: #D8DEE3; }
+
+a.headerlink {
+ color: #c60f0f;
+ font-size: 0.8em;
+ padding: 0 4px 0 4px;
+ text-decoration: none;
+}
+
+a.headerlink:hover {
+ background-color: #c60f0f;
+ color: white;
+}
+
+div.body p, div.body dd, div.body li {
+ line-height: 1.5em;
+}
+
+div.admonition p.admonition-title + p {
+ display: inline;
+}
+
+div.highlight{
+ background-color: white;
+}
+
+div.note {
+ background-color: #eee;
+ border: 1px solid #ccc;
+}
+
+div.seealso {
+ background-color: #ffc;
+ border: 1px solid #ff6;
+}
+
+div.topic {
+ background-color: #eee;
+}
+
+div.warning {
+ background-color: #ffe4e4;
+ border: 1px solid #f66;
+}
+
+p.admonition-title {
+ display: inline;
+}
+
+p.admonition-title:after {
+ content: ":";
+}
+
+pre {
+ padding: 10px;
+ background-color: White;
+ color: #222;
+ line-height: 1.2em;
+ border: 1px solid #C6C9CB;
+ font-size: 1.1em;
+ margin: 1.5em 0 1.5em 0;
+ -webkit-box-shadow: 1px 1px 1px #d8d8d8;
+ -moz-box-shadow: 1px 1px 1px #d8d8d8;
+}
+
+tt {
+ background-color: #ecf0f3;
+ color: #222;
+ /* padding: 1px 2px; */
+ font-size: 1.1em;
+ font-family: monospace;
+}
+
+.viewcode-back {
+ font-family: Arial, sans-serif;
+}
+
+div.viewcode-block:target {
+ background-color: #f4debf;
+ border-top: 1px solid #ac9;
+ border-bottom: 1px solid #ac9;
+} \ No newline at end of file
diff --git a/docs/_static/noise_dk.png b/docs/_static/noise_dk.png
new file mode 100644
index 00000000..f0b4de42
--- /dev/null
+++ b/docs/_static/noise_dk.png
Binary files differ
diff --git a/docs/_static/sphinxdoc.css b/docs/_static/sphinxdoc.css
new file mode 100644
index 00000000..0a428074
--- /dev/null
+++ b/docs/_static/sphinxdoc.css
@@ -0,0 +1,339 @@
+/*
+ * sphinxdoc.css_t
+ * ~~~~~~~~~~~~~~~
+ *
+ * Sphinx stylesheet -- sphinxdoc theme. Originally created by
+ * Armin Ronacher for Werkzeug.
+ *
+ * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
+ * :license: BSD, see LICENSE for details.
+ *
+ */
+
+@import url("basic.css");
+
+/* -- page layout ----------------------------------------------------------- */
+
+body {
+ font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
+ 'Verdana', sans-serif;
+ font-size: 14px;
+ letter-spacing: -0.01em;
+ line-height: 150%;
+ text-align: center;
+ background-color: #BFD1D4;
+ color: black;
+ padding: 0;
+ border: 1px solid #aaa;
+
+ margin: 0px 80px 0px 80px;
+ min-width: 740px;
+}
+
+div.document {
+ background-color: white;
+ text-align: left;
+ background-image: url(contents.png);
+ background-repeat: repeat-x;
+}
+
+div.bodywrapper {
+ margin: 0 240px 0 0;
+ border-right: 1px solid #ccc;
+}
+
+div.body {
+ margin: 0;
+ padding: 0.5em 20px 20px 20px;
+}
+
+div.related {
+ font-size: 1em;
+}
+
+div.related ul {
+ background-image: url(navigation.png);
+ height: 2em;
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+}
+
+div.related ul li {
+ margin: 0;
+ padding: 0;
+ height: 2em;
+ float: left;
+}
+
+div.related ul li.right {
+ float: right;
+ margin-right: 5px;
+}
+
+div.related ul li a {
+ margin: 0;
+ padding: 0 5px 0 5px;
+ line-height: 1.75em;
+ color: #EE9816;
+}
+
+div.related ul li a:hover {
+ color: #3CA8E7;
+}
+
+div.sphinxsidebarwrapper {
+ padding: 0;
+}
+
+div.sphinxsidebar {
+ margin: 0;
+ padding: 0.5em 15px 15px 0;
+ width: 210px;
+ float: right;
+ font-size: 1em;
+ text-align: left;
+}
+
+div.sphinxsidebar h3, div.sphinxsidebar h4 {
+ margin: 1em 0 0.5em 0;
+ font-size: 1em;
+ padding: 0.1em 0 0.1em 0.5em;
+ color: white;
+ border: 1px solid #86989B;
+ background-color: #AFC1C4;
+}
+
+div.sphinxsidebar h3 a {
+ color: white;
+}
+
+div.sphinxsidebar ul {
+ padding-left: 1.5em;
+ margin-top: 7px;
+ padding: 0;
+ line-height: 130%;
+}
+
+div.sphinxsidebar ul ul {
+ margin-left: 20px;
+}
+
+div.footer {
+ background-color: #E3EFF1;
+ color: #86989B;
+ padding: 3px 8px 3px 0;
+ clear: both;
+ font-size: 0.8em;
+ text-align: right;
+}
+
+div.footer a {
+ color: #86989B;
+ text-decoration: underline;
+}
+
+/* -- body styles ----------------------------------------------------------- */
+
+p {
+ margin: 0.8em 0 0.5em 0;
+}
+
+a {
+ color: #CA7900;
+ text-decoration: none;
+}
+
+a:hover {
+ color: #2491CF;
+}
+
+div.body a {
+ text-decoration: underline;
+}
+
+h1 {
+ margin: 0;
+ padding: 0.7em 0 0.3em 0;
+ font-size: 1.5em;
+ color: #11557C;
+}
+
+h2 {
+ margin: 1.3em 0 0.2em 0;
+ font-size: 1.35em;
+ padding: 0;
+}
+
+h3 {
+ margin: 1em 0 -0.3em 0;
+ font-size: 1.2em;
+}
+
+div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a {
+ color: black!important;
+}
+
+h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor {
+ display: none;
+ margin: 0 0 0 0.3em;
+ padding: 0 0.2em 0 0.2em;
+ color: #aaa!important;
+}
+
+h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor,
+h5:hover a.anchor, h6:hover a.anchor {
+ display: inline;
+}
+
+h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover,
+h5 a.anchor:hover, h6 a.anchor:hover {
+ color: #777;
+ background-color: #eee;
+}
+
+a.headerlink {
+ color: #c60f0f!important;
+ font-size: 1em;
+ margin-left: 6px;
+ padding: 0 4px 0 4px;
+ text-decoration: none!important;
+}
+
+a.headerlink:hover {
+ background-color: #ccc;
+ color: white!important;
+}
+
+cite, code, tt {
+ font-family: 'Consolas', 'Deja Vu Sans Mono',
+ 'Bitstream Vera Sans Mono', monospace;
+ font-size: 0.95em;
+ letter-spacing: 0.01em;
+}
+
+tt {
+ background-color: #f2f2f2;
+ border-bottom: 1px solid #ddd;
+ color: #333;
+}
+
+tt.descname, tt.descclassname, tt.xref {
+ border: 0;
+}
+
+hr {
+ border: 1px solid #abc;
+ margin: 2em;
+}
+
+a tt {
+ border: 0;
+ color: #CA7900;
+}
+
+a tt:hover {
+ color: #2491CF;
+}
+
+pre {
+ font-family: 'Consolas', 'Deja Vu Sans Mono',
+ 'Bitstream Vera Sans Mono', monospace;
+ font-size: 0.95em;
+ letter-spacing: 0.015em;
+ line-height: 120%;
+ padding: 0.5em;
+ border: 1px solid #ccc;
+ background-color: #f8f8f8;
+}
+
+pre a {
+ color: inherit;
+ text-decoration: underline;
+}
+
+td.linenos pre {
+ padding: 0.5em 0;
+}
+
+div.quotebar {
+ background-color: #f8f8f8;
+ max-width: 250px;
+ float: right;
+ padding: 2px 7px;
+ border: 1px solid #ccc;
+}
+
+div.topic {
+ background-color: #f8f8f8;
+}
+
+table {
+ border-collapse: collapse;
+ margin: 0 -0.5em 0 -0.5em;
+}
+
+table td, table th {
+ padding: 0.2em 0.5em 0.2em 0.5em;
+}
+
+div.admonition, div.warning {
+ font-size: 0.9em;
+ margin: 1em 0 1em 0;
+ border: 1px solid #86989B;
+ background-color: #f7f7f7;
+ padding: 0;
+}
+
+div.admonition p, div.warning p {
+ margin: 0.5em 1em 0.5em 1em;
+ padding: 0;
+}
+
+div.admonition pre, div.warning pre {
+ margin: 0.4em 1em 0.4em 1em;
+}
+
+div.admonition p.admonition-title,
+div.warning p.admonition-title {
+ margin: 0;
+ padding: 0.1em 0 0.1em 0.5em;
+ color: white;
+ border-bottom: 1px solid #86989B;
+ font-weight: bold;
+ background-color: #AFC1C4;
+}
+
+div.warning {
+ border: 1px solid #940000;
+}
+
+div.warning p.admonition-title {
+ background-color: #CF0000;
+ border-bottom-color: #940000;
+}
+
+div.admonition ul, div.admonition ol,
+div.warning ul, div.warning ol {
+ margin: 0.1em 0.5em 0.5em 3em;
+ padding: 0;
+}
+
+div.versioninfo {
+ margin: 1em 0 0 0;
+ border: 1px solid #ccc;
+ background-color: #DDEAF0;
+ padding: 8px;
+ line-height: 1.3em;
+ font-size: 0.9em;
+}
+
+.viewcode-back {
+ font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
+ 'Verdana', sans-serif;
+}
+
+div.viewcode-block:target {
+ background-color: #f4debf;
+ border-top: 1px solid #ac9;
+ border-bottom: 1px solid #ac9;
+}
diff --git a/docs/api/basexmpp.rst b/docs/api/basexmpp.rst
new file mode 100644
index 00000000..fa96322e
--- /dev/null
+++ b/docs/api/basexmpp.rst
@@ -0,0 +1,8 @@
+========
+BaseXMPP
+========
+
+.. module:: sleekxmpp.basexmpp
+
+.. autoclass:: BaseXMPP
+ :members:
diff --git a/docs/api/clientxmpp.rst b/docs/api/clientxmpp.rst
new file mode 100644
index 00000000..a6f32c43
--- /dev/null
+++ b/docs/api/clientxmpp.rst
@@ -0,0 +1,8 @@
+==========
+ClientXMPP
+==========
+
+.. module:: sleekxmpp.clientxmpp
+
+.. autoclass:: ClientXMPP
+ :members:
diff --git a/docs/api/componentxmpp.rst b/docs/api/componentxmpp.rst
new file mode 100644
index 00000000..989120c2
--- /dev/null
+++ b/docs/api/componentxmpp.rst
@@ -0,0 +1,8 @@
+=============
+ComponentXMPP
+=============
+
+.. module:: sleekxmpp.componentxmpp
+
+.. autoclass:: ComponentXMPP
+ :members:
diff --git a/docs/api/exceptions.rst b/docs/api/exceptions.rst
new file mode 100644
index 00000000..7bc72ce5
--- /dev/null
+++ b/docs/api/exceptions.rst
@@ -0,0 +1,14 @@
+Exceptions
+==========
+
+.. module:: sleekxmpp.exceptions
+
+
+.. autoexception:: XMPPError
+ :members:
+
+.. autoexception:: IqError
+ :members:
+
+.. autoexception:: IqTimeout
+ :members:
diff --git a/docs/api/xmlstream/filesocket.rst b/docs/api/xmlstream/filesocket.rst
new file mode 100644
index 00000000..35f44019
--- /dev/null
+++ b/docs/api/xmlstream/filesocket.rst
@@ -0,0 +1,12 @@
+.. module:: sleekxmpp.xmlstream.filesocket
+
+.. _filesocket:
+
+Python 2.6 File Socket Shims
+============================
+
+.. autoclass:: FileSocket
+ :members:
+
+.. autoclass:: Socket26
+ :members:
diff --git a/docs/api/xmlstream/handler.rst b/docs/api/xmlstream/handler.rst
new file mode 100644
index 00000000..33c0bf42
--- /dev/null
+++ b/docs/api/xmlstream/handler.rst
@@ -0,0 +1,24 @@
+Stanza Handlers
+===============
+
+The Basic Handler
+-----------------
+.. module:: sleekxmpp.xmlstream.handler.base
+
+.. autoclass:: BaseHandler
+ :members:
+
+Callback
+--------
+.. module:: sleekxmpp.xmlstream.handler.callback
+
+.. autoclass:: Callback
+ :members:
+
+
+Waiter
+------
+.. module:: sleekxmpp.xmlstream.handler.waiter
+
+.. autoclass:: Waiter
+ :members:
diff --git a/docs/api/xmlstream/jid.rst b/docs/api/xmlstream/jid.rst
new file mode 100644
index 00000000..22a2db45
--- /dev/null
+++ b/docs/api/xmlstream/jid.rst
@@ -0,0 +1,7 @@
+Jabber IDs (JID)
+=================
+
+.. module:: sleekxmpp.xmlstream.jid
+
+.. autoclass:: JID
+ :members:
diff --git a/docs/api/xmlstream/matcher.rst b/docs/api/xmlstream/matcher.rst
new file mode 100644
index 00000000..df3591bc
--- /dev/null
+++ b/docs/api/xmlstream/matcher.rst
@@ -0,0 +1,41 @@
+Stanza Matchers
+===============
+
+The Basic Matcher
+-----------------
+.. module:: sleekxmpp.xmlstream.matcher.base
+
+.. autoclass:: MatcherBase
+ :members:
+
+
+ID Matching
+-----------
+.. module:: sleekxmpp.xmlstream.matcher.id
+
+.. autoclass:: MatcherId
+ :members:
+
+
+Stanza Path Matching
+--------------------
+.. module:: sleekxmpp.xmlstream.matcher.stanzapath
+
+.. autoclass:: StanzaPath
+ :members:
+
+
+XPath
+-----
+.. module:: sleekxmpp.xmlstream.matcher.xpath
+
+.. autoclass:: MatchXPath
+ :members:
+
+
+XMLMask
+-------
+.. module:: sleekxmpp.xmlstream.matcher.xmlmask
+
+.. autoclass:: MatchXMLMask
+ :members:
diff --git a/docs/api/xmlstream/scheduler.rst b/docs/api/xmlstream/scheduler.rst
new file mode 100644
index 00000000..ff91701e
--- /dev/null
+++ b/docs/api/xmlstream/scheduler.rst
@@ -0,0 +1,11 @@
+=========
+Scheduler
+=========
+
+.. module:: sleekxmpp.xmlstream.scheduler
+
+.. autoclass:: Task
+ :members:
+
+.. autoclass:: Scheduler
+ :members:
diff --git a/docs/api/xmlstream/stanzabase.rst b/docs/api/xmlstream/stanzabase.rst
new file mode 100644
index 00000000..f575299e
--- /dev/null
+++ b/docs/api/xmlstream/stanzabase.rst
@@ -0,0 +1,123 @@
+.. _stanzabase:
+
+==============
+Stanza Objects
+==============
+
+.. module:: sleekxmpp.xmlstream.stanzabase
+
+The :mod:`~sleekmxpp.xmlstream.stanzabase` module provides a wrapper for the
+standard :mod:`~xml.etree.ElementTree` module that makes working with XML
+less painful. Instead of having to manually move up and down an element
+tree and insert subelements and attributes, you can interact with an object
+that behaves like a normal dictionary or JSON object, which silently maps
+keys to XML attributes and elements behind the scenes.
+
+Overview
+--------
+
+The usefulness of this layer grows as the XML you have to work with
+becomes nested. The base unit here, :class:`ElementBase`, can map to a
+single XML element, or several depending on how advanced of a mapping
+is desired from interface keys to XML structures. For example, a single
+:class:`ElementBase` derived class could easily describe:
+
+.. code-block:: xml
+
+ <message to="user@example.com" from="friend@example.com">
+ <body>Hi!</body>
+ <x:extra>
+ <x:item>Custom item 1</x:item>
+ <x:item>Custom item 2</x:item>
+ <x:item>Custom item 3</x:item>
+ </x:extra>
+ </message>
+
+If that chunk of XML were put in the :class:`ElementBase` instance
+``msg``, we could extract the data from the XML using::
+
+ >>> msg['extra']
+ ['Custom item 1', 'Custom item 2', 'Custom item 3']
+
+Provided we set up the handler for the ``'extra'`` interface to load the
+``<x:item>`` element content into a list.
+
+The key concept is that given an XML structure that will be repeatedly
+used, we can define a set of :term:`interfaces` which when we read from,
+write to, or delete, will automatically manipulate the underlying XML
+as needed. In addition, some of these interfaces may in turn reference
+child objects which expose interfaces for particularly complex child
+elements of the original XML chunk.
+
+.. seealso::
+ :ref:`create-stanza-interfaces`.
+
+Because the :mod:`~sleekxmpp.xmlstream.stanzabase` module was developed
+as part of an `XMPP <http://xmpp.org>`_ library, these chunks of XML are
+referred to as :term:`stanzas <stanza>`, and in SleekXMPP we refer to a
+subclass of :class:`ElementBase` which defines the interfaces needed for
+interacting with a given :term:`stanza` a :term:`stanza object`.
+
+To make dealing with more complicated and nested :term:`stanzas <stanza>`
+or XML chunks easier, :term:`stanza objects <stanza object>` can be
+composed in two ways: as iterable child objects or as plugins. Iterable
+child stanzas, or :term:`substanzas`, are accessible through a special
+``'substanzas'`` interface. This option is useful for stanzas which
+may contain more than one of the same kind of element. When there is
+only one child element, the plugin method is more useful. For plugins,
+a parent stanza object delegates one of its XML child elements to the
+plugin stanza object. Here is an example:
+
+.. code-block:: xml
+
+ <iq type="result">
+ <query xmlns="http://jabber.org/protocol/disco#info">
+ <identity category="client" type="bot" name="SleekXMPP Bot" />
+ </query>
+ </iq>
+
+We can can arrange this stanza into two objects: an outer, wrapper object for
+dealing with the ``<iq />`` element and its attributes, and a plugin object to
+control the ``<query />`` payload element. If we give the plugin object the
+name ``'disco_info'`` (using its :attr:`ElementBase.plugin_attrib` value), then
+we can access the plugin as so::
+
+ >>> iq['disco_info']
+ '<query xmlns="http://jabber.org/protocol/disco#info">
+ <identity category="client" type="bot" name="SleekXMPP Bot" />
+ </query>'
+
+We can then drill down through the plugin object's interfaces as desired::
+
+ >>> iq['disco_info']['identities']
+ [('client', 'bot', 'SleekXMPP Bot')]
+
+Plugins may also add new interfaces to the parent stanza object as if they
+had been defined by the parent directly, and can also override the behaviour
+of an interface defined by the parent.
+
+.. seealso::
+
+ - :ref:`create-stanza-plugins`
+ - :ref:`create-extension-plugins`
+ - :ref:`override-parent-interfaces`
+
+
+Registering Stanza Plugins
+--------------------------
+
+.. autofunction:: register_stanza_plugin
+
+ElementBase
+-----------
+
+.. autoclass:: ElementBase
+ :members:
+ :private-members:
+ :special-members:
+
+StanzaBase
+----------
+
+.. autoclass:: StanzaBase
+ :members:
diff --git a/docs/api/xmlstream/tostring.rst b/docs/api/xmlstream/tostring.rst
new file mode 100644
index 00000000..82a8c2a5
--- /dev/null
+++ b/docs/api/xmlstream/tostring.rst
@@ -0,0 +1,46 @@
+.. module:: sleekxmpp.xmlstream.tostring
+
+.. _tostring:
+
+XML Serialization
+=================
+
+Since the XML layer of SleekXMPP is based on :mod:`~xml.etree.ElementTree`,
+why not just use the built-in :func:`~xml.etree.ElementTree.tostring`
+method? The answer is that using that method produces ugly results when
+using namespaces. The :func:`tostring()` method used here intelligently
+hides namespaces when able and does not introduce excessive namespace
+prefixes::
+
+ >>> from sleekxmpp.xmlstream.tostring import tostring
+ >>> from xml.etree import cElementTree as ET
+ >>> xml = ET.fromstring('<foo xmlns="bar"><baz /></foo>')
+ >>> ET.tostring(xml)
+ '<ns0:foo xmlns:ns0="bar"><ns0:baz /></foo>'
+ >>> tostring(xml)
+ '<foo xmlns="bar"><baz /></foo>'
+
+As a side effect of this namespace hiding, using :func:`tostring()` may
+produce unexpected results depending on how the :func:`tostring()` method
+is invoked. For example, when sending XML on the wire, the main XMPP
+stanzas with their namespace of ``jabber:client`` will not include the
+namespace because that is already declared by the stream header. But, if
+you create a :class:`~sleekxmpp.stanza.message.Message` instance and dump
+it to the terminal, the ``jabber:client`` namespace will appear.
+
+.. autofunction:: tostring
+
+Escaping Special Characters
+---------------------------
+
+In order to prevent errors when sending arbitrary text as the textual
+content of an XML element, certain characters must be escaped. These
+are: ``&``, ``<``, ``>``, ``"``, and ``'``. The default escaping
+mechanism is to replace those characters with their equivalent escape
+entities: ``&amp;``, ``&lt;``, ``&gt;``, ``&apos;``, and ``&quot;``.
+
+In the future, the use of CDATA sections may be allowed to reduce the
+size of escaped text or for when other XMPP processing agents do not
+undertand these entities.
+
+.. autofunction:: xml_escape
diff --git a/docs/api/xmlstream/xmlstream.rst b/docs/api/xmlstream/xmlstream.rst
new file mode 100644
index 00000000..90a7a6af
--- /dev/null
+++ b/docs/api/xmlstream/xmlstream.rst
@@ -0,0 +1,10 @@
+==========
+XML Stream
+==========
+
+.. module:: sleekxmpp.xmlstream.xmlstream
+
+.. autoexception:: RestartStream
+
+.. autoclass:: XMLStream
+ :members:
diff --git a/docs/architecture.rst b/docs/architecture.rst
new file mode 100644
index 00000000..a2e0a27d
--- /dev/null
+++ b/docs/architecture.rst
@@ -0,0 +1,177 @@
+.. index:: XMLStream, BaseXMPP, ClientXMPP, ComponentXMPP
+
+SleekXMPP Architecture
+======================
+
+The core of SleekXMPP is contained in four classes: ``XMLStream``,
+``BaseXMPP``, ``ClientXMPP``, and ``ComponentXMPP``. Along side this
+stack is a library for working with XML objects that eliminates most
+of the tedium of creating/manipulating XML.
+
+.. image:: _static/images/arch_layers.png
+ :height: 300px
+ :align: center
+
+
+.. index:: XMLStream
+
+The Foundation: XMLStream
+-------------------------
+:class:`~sleekxmpp.xmlstream.xmlstream.XMLStream` is a mostly XMPP-agnostic
+class whose purpose is to read and write from a bi-directional XML stream.
+It also allows for callback functions to execute when XML matching given
+patterns is received; these callbacks are also referred to as :term:`stream
+handlers <stream handler>`. The class also provides a basic eventing system
+which can be triggered either manually or on a timed schedule.
+
+The Main Threads
+~~~~~~~~~~~~~~~~
+:class:`~sleekxmpp.xmlstream.xmlstream.XMLStream` instances run using at
+least three background threads: the send thread, the read thread, and the
+scheduler thread. The send thread is in charge of monitoring the send queue
+and writing text to the outgoing XML stream. The read thread pulls text off
+of the incoming XML stream and stores the results in an event queue. The
+scheduler thread is used to emit events after a given period of time.
+
+Additionally, the main event processing loop may be executed in its
+own thread if SleekXMPP is being used in the background for another
+application.
+
+Short-lived threads may also be spawned as requested for threaded
+:term:`event handlers <event handler>`.
+
+How XML Text is Turned into Action
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+To demonstrate the flow of information, let's consider what happens
+when this bit of XML is received (with an assumed namespace of
+``jabber:client``):
+
+.. code-block:: xml
+
+ <message to="user@example.com" from="friend@example.net">
+ <body>Hej!</body>
+ </message>
+
+
+1. **Convert XML strings into objects.**
+
+ Incoming text is parsed and converted into XML objects (using
+ ElementTree) which are then wrapped into what are referred to as
+ :term:`Stanza objects <stanza object>`. The appropriate class for the
+ new object is determined using a map of namespaced element names to
+ classes.
+
+ Our incoming XML is thus turned into a :class:`~sleekxmpp.stanza.Message`
+ :term:`stanza object` because the namespaced element name
+ ``{jabber:client}message`` is associated with the class
+ :class:`~sleekxmpp.stanza.Message`.
+
+2. **Match stanza objects to callbacks.**
+
+ These objects are then compared against the stored patterns associated
+ with the registered callback handlers. For each match, a copy of the
+ :term:`stanza object` is paired with a reference to the handler and
+ placed into the event queue.
+
+ Our :class:`~sleekxmpp.stanza.Message` object is thus paired with the message stanza handler
+ :meth:`BaseXMPP._handle_message` to create the tuple::
+
+ ('stanza', stanza_obj, handler)
+
+3. **Process the event queue.**
+
+ The event queue is the heart of SleekXMPP. Nearly every action that
+ takes place is first inserted into this queue, whether that be received
+ stanzas, custom events, or scheduled events.
+
+ When the stanza is pulled out of the event queue with an associated
+ callback, the callback function is executed with the stanza as its only
+ parameter.
+
+ .. warning::
+ The callback, aka :term:`stream handler`, is executed in the main event
+ processing thread. If the handler blocks, event processing will also
+ block.
+
+4. **Raise Custom Events**
+
+ Since a :term:`stream handler` shouldn't block, if extensive processing
+ for a stanza is required (such as needing to send and receive an
+ :class:`~sleekxmpp.stanza.Iq` stanza), then custom events must be used.
+ These events are not explicitly tied to the incoming XML stream and may
+ be raised at any time. Importantly, these events may be handled in their
+ own thread.
+
+ When the event is raised, a copy of the stanza is created for each
+ handler registered for the event. In contrast to :term:`stream handlers
+ <stream handler>`, these functions are referred to as :term:`event
+ handlers <event handler>`. Each stanza/handler pair is then put into the
+ event queue.
+
+ .. note::
+ It is possible to skip the event queue and process an event immediately
+ by using ``direct=True`` when raising the event.
+
+ The code for :meth:`BaseXMPP._handle_message` follows this pattern, and
+ raises a ``'message'`` event::
+
+ self.event('message', msg)
+
+ The event call then places the message object back into the event queue
+ paired with an :term:`event handler`::
+
+ ('event', 'message', msg_copy1, custom_event_handler_1)
+ ('event', 'message', msg_copy2, custom_evetn_handler_2)
+
+5. **Process Custom Events**
+
+ The stanza and :term:`event handler` are then pulled from the event
+ queue, and the handler is executed, passing the stanza as its only
+ argument. If the handler was registered as threaded, then a new thread
+ will be spawned for it.
+
+ .. note::
+ Events may be raised without needing :term:`stanza objects <stanza object>`.
+ For example, you could use ``self.event('custom', {'a': 'b'})``.
+ You don't even need any arguments: ``self.event('no_parameters')``.
+ However, every event handler MUST accept at least one argument.
+
+ Finally, after a long trek, our message is handed off to the user's
+ custom handler in order to do awesome stuff::
+
+ msg.reply()
+ msg['body'] = "Hey! This is awesome!"
+ msg.send()
+
+
+.. index:: BaseXMPP, XMLStream
+
+Raising XMPP Awareness: BaseXMPP
+--------------------------------
+While :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream` attempts to shy away
+from anything too XMPP specific, :class:`~sleekxmpp.basexmpp.BaseXMPP`'s
+sole purpose is to provide foundational support for sending and receiving
+XMPP stanzas. This support includes registering the basic message,
+presence, and iq stanzas, methods for creating and sending stanzas, and
+default handlers for incoming messages and keeping track of presence
+notifications.
+
+The plugin system for adding new XEP support is also maintained by
+:class:`~sleekxmpp.basexmpp.BaseXMPP`.
+
+.. index:: ClientXMPP, BaseXMPP
+
+ClientXMPP
+----------
+:class:`~sleekxmpp.clientxmpp.ClientXMPP` extends
+:class:`~sleekxmpp.clientxmpp.BaseXMPP` with additional logic for connecting
+to an XMPP server by performing DNS lookups. It also adds support for stream
+features such as STARTTLS and SASL.
+
+.. index:: ComponentXMPP, BaseXMPP
+
+ComponentXMPP
+-------------
+:class:`~sleekxmpp.componentxmpp.ComponentXMPP` is only a thin layer on top of
+:class:`~sleekxmpp.basexmpp.BaseXMPP` that implements the component handshake
+protocol.
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 00000000..dd83f243
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,222 @@
+# -*- coding: utf-8 -*-
+#
+# SleekXMPP documentation build configuration file, created by
+# sphinx-quickstart on Tue Aug 9 22:27:06 2011.
+#
+# 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, 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', 'sphinx.ext.intersphinx']
+
+# 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'SleekXMPP'
+copyright = u'2011, Nathan Fritz, Lance Stout'
+
+# 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 = '1.0'
+# The full version, including alpha/beta/rc tags.
+release = '1.0RC3'
+
+# 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 = 'tango'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- 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 = 'nature'
+
+# 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 = {'headingcolor': '#CFCFCF', 'linkcolor': '#4A7389'}
+
+# 00ADEE
+
+# 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 = 'SleekXMPP'
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+html_short_title = '%s Documentation' % release
+
+# 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']
+
+# 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 = 'SleekXMPPdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index', 'SleekXMPP.tex', u'SleekXMPP Documentation',
+ u'Nathan Fritz, Lance Stout', '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
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# 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', 'sleekxmpp', u'SleekXMPP Documentation',
+ [u'Nathan Fritz, Lance Stout'], 1)
+]
+
+intersphinx_mapping = {'python': ('http://docs.python.org/3.2', 'python-objects.inv')}
diff --git a/docs/create_plugin.rst b/docs/create_plugin.rst
new file mode 100644
index 00000000..12efa84c
--- /dev/null
+++ b/docs/create_plugin.rst
@@ -0,0 +1,679 @@
+.. _create-plugin:
+
+Creating a SleekXMPP Plugin
+===========================
+
+One of the goals of SleekXMPP is to provide support for every draft or final
+XMPP extension (`XEP <http://xmpp.org/extensions/>`_). To do this, SleekXMPP has a
+plugin mechanism for adding the functionalities required by each XEP. But even
+though plugins were made to quickly implement and prototype the official XMPP
+extensions, there is no reason you can't create your own plugin to implement
+your own custom XMPP-based protocol.
+
+This guide will help walk you through the steps to
+implement a rudimentary version of `XEP-0077 In-band
+Registration <http://xmpp.org/extensions/xep-0077.html>`_. In-band registration
+was implemented in example 14-6 (page 223) of `XMPP: The Definitive
+Guide <http://oreilly.com/catalog/9780596521271>`_ because there was no SleekXMPP
+plugin for XEP-0077 at the time of writing. We will partially fix that issue
+here by turning the example implementation from *XMPP: The Definitive Guide*
+into a plugin. Again, note that this will not a complete implementation, and a
+different, more robust, official plugin for XEP-0077 may be added to SleekXMPP
+in the future.
+
+.. note::
+
+ The example plugin created in this guide is for the server side of the
+ registration process only. It will **NOT** be able to register new accounts
+ on an XMPP server.
+
+First Steps
+-----------
+Every plugin inherits from the class :mod:`base_plugin <sleekxmpp.plugins.base.base_plugin>`,
+and must include a ``plugin_init`` method. While the
+plugins distributed with SleekXMPP must be placed in the plugins directory
+``sleekxmpp/plugins`` to be loaded, custom plugins may be loaded from any
+module. To do so, use the following form when registering the plugin:
+
+.. code-block:: python
+
+ self.register_plugin('myplugin', module=mod_containing_my_plugin)
+
+The plugin name must be the same as the plugin's class name.
+
+Now, we can open our favorite text editors and create ``xep_0077.py`` in
+``SleekXMPP/sleekxmpp/plugins``. We want to do some basic house-keeping and
+declare the name and description of the XEP we are implementing. If you
+are creating your own custom plugin, you don't need to include the ``xep``
+attribute.
+
+.. code-block:: python
+
+ """
+ Creating a SleekXMPP Plugin
+
+ This is a minimal implementation of XEP-0077 to serve
+ as a tutorial for creating SleekXMPP plugins.
+ """
+
+ from sleekxmpp.plugins.base import base_plugin
+
+ class xep_0077(base_plugin):
+ """
+ XEP-0077 In-Band Registration
+ """
+
+ def plugin_init(self):
+ self.description = "In-Band Registration"
+ self.xep = "0077"
+
+Now that we have a basic plugin, we need to edit
+``sleekxmpp/plugins/__init__.py`` to include our new plugin by adding
+``'xep_0077'`` to the ``__all__`` declaration.
+
+Interacting with Other Plugins
+------------------------------
+
+In-band registration is a feature that should be advertised through `Service
+Discovery <http://xmpp.org/extensions/xep-0030.html>`_. To do that, we tell the
+``xep_0030`` plugin to add the ``"jabber:iq:register"`` feature. We put this
+call in a method named ``post_init`` which will be called once the plugin has
+been loaded; by doing so we advertise that we can do registrations only after we
+finish activating the plugin.
+
+The ``post_init`` method needs to call ``base_plugin.post_init(self)``
+which will mark that ``post_init`` has been called for the plugin. Once the
+SleekXMPP object begins processing, ``post_init`` will be called on any plugins
+that have not already run ``post_init``. This allows you to register plugins and
+their dependencies without needing to worry about the order in which you do so.
+
+**Note:** by adding this call we have introduced a dependency on the XEP-0030
+plugin. Be sure to register ``'xep_0030'`` as well as ``'xep_0077'``. SleekXMPP
+does not automatically load plugin dependencies for you.
+
+.. code-block:: python
+
+ def post_init(self):
+ base_plugin.post_init(self)
+ self.xmpp['xep_0030'].add_feature("jabber:iq:register")
+
+Creating Custom Stanza Objects
+------------------------------
+
+Now, the IQ stanzas needed to implement our version of XEP-0077 are not very
+complex, and we could just interact with the XML objects directly just like
+in the *XMPP: The Definitive Guide* example. However, creating custom stanza
+objects is good practice.
+
+We will create a new ``Registration`` stanza. Following the *XMPP: The
+Definitive Guide* example, we will add support for a username and password
+field. We also need two flags: ``registered`` and ``remove``. The ``registered``
+flag is sent when an already registered user attempts to register, along with
+their registration data. The ``remove`` flag is a request to unregister a user's
+account.
+
+Adding additional `fields specified in
+XEP-0077 <http://xmpp.org/extensions/xep-0077.html#registrar-formtypes-register>`_
+will not be difficult and is left as an exercise for the reader.
+
+Our ``Registration`` class needs to start with a few descriptions of its
+behaviour:
+
+* ``namespace``
+ The namespace our stanza object lives in. In this case,
+ ``"jabber:iq:register"``.
+
+* ``name``
+ The name of the root XML element. In this case, the ``query`` element.
+
+* ``plugin_attrib``
+ The name to access this type of stanza. In particular, given a
+ registration stanza, the ``Registration`` object can be found using:
+ ``iq_object['register']``.
+
+* ``interfaces``
+ A list of dictionary-like keys that can be used with the stanza object.
+ When using ``"key"``, if there exists a method of the form ``getKey``,
+ ``setKey``, or``delKey`` (depending on context) then the result of calling
+ that method will be returned. Otherwise, the value of the attribute ``key``
+ of the main stanza element is returned if one exists.
+
+ **Note:** The accessor methods currently use title case, and not camel case.
+ Thus if you need to access an item named ``"methodName"`` you will need to
+ use ``getMethodname``. This naming convention might change to full camel
+ case in a future version of SleekXMPP.
+
+* ``sub_interfaces``
+ A subset of ``interfaces``, but these keys map to the text of any
+ subelements that are direct children of the main stanza element. Thus,
+ referencing ``iq_object['register']['username']`` will either execute
+ ``getUsername`` or return the value in the ``username`` element of the
+ query.
+
+ If you need to access an element, say ``elem``, that is not a direct child
+ of the main stanza element, you will need to add ``getElem``, ``setElem``,
+ and ``delElem``. See the note above about naming conventions.
+
+.. code-block:: python
+
+ from sleekxmpp.xmlstream import ElementBase, ET, JID, register_stanza_plugin
+ from sleekxmpp import Iq
+
+ class Registration(ElementBase):
+ namespace = 'jabber:iq:register'
+ name = 'query'
+ plugin_attrib = 'register'
+ interfaces = set(('username', 'password', 'registered', 'remove'))
+ sub_interfaces = interfaces
+
+ def getRegistered(self):
+ present = self.xml.find('{%s}registered' % self.namespace)
+ return present is not None
+
+ def getRemove(self):
+ present = self.xml.find('{%s}remove' % self.namespace)
+ return present is not None
+
+ def setRegistered(self, registered):
+ if registered:
+ self.addField('registered')
+ else:
+ del self['registered']
+
+ def setRemove(self, remove):
+ if remove:
+ self.addField('remove')
+ else:
+ del self['remove']
+
+ def addField(self, name):
+ itemXML = ET.Element('{%s}%s' % (self.namespace, name))
+ self.xml.append(itemXML)
+
+Setting a ``sub_interface`` attribute to ``""`` will remove that subelement.
+Since we want to include empty registration fields in our form, we need the
+``addField`` method to add the empty elements.
+
+Since the ``registered`` and ``remove`` elements are just flags, we need to add
+custom logic to enforce the binary behavior.
+
+Extracting Stanzas from the XML Stream
+--------------------------------------
+
+Now that we have a custom stanza object, we need to be able to detect when we
+receive one. To do this, we register a stream handler that will pattern match
+stanzas off of the XML stream against our stanza object's element name and
+namespace. To do so, we need to create a ``Callback`` object which contains
+an XML fragment that can identify our stanza type. We can add this handler
+registration to our ``plugin_init`` method.
+
+Also, we need to associate our ``Registration`` class with IQ stanzas;
+that requires the use of the ``register_stanza_plugin`` function (in
+``sleekxmpp.xmlstream.stanzabase``) which takes the class of a parent stanza
+type followed by the substanza type. In our case, the parent stanza is an IQ
+stanza, and the substanza is our registration query.
+
+The ``__handleRegistration`` method referenced in the callback will be our
+handler function to process registration requests.
+
+.. code-block:: python
+
+ def plugin_init(self):
+ self.description = "In-Band Registration"
+ self.xep = "0077"
+
+ self.xmpp.registerHandler(
+ Callback('In-Band Registration',
+ MatchXPath('{%s}iq/{jabber:iq:register}query' % self.xmpp.default_ns),
+ self.__handleRegistration))
+ register_stanza_plugin(Iq, Registration)
+
+Handling Incoming Stanzas and Triggering Events
+-----------------------------------------------
+There are six situations that we need to handle to finish our implementation of
+XEP-0077.
+
+**Registration Form Request from a New User:**
+
+ .. code-block:: xml
+
+ <iq type="result">
+ <query xmlns="jabber:iq:register">
+ <username />
+ <password />
+ </query>
+ </iq>
+
+**Registration Form Request from an Existing User:**
+
+ .. code-block:: xml
+
+ <iq type="result">
+ <query xmlns="jabber:iq:register">
+ <registered />
+ <username>Foo</username>
+ <password>hunter2</password>
+ </query>
+ </iq>
+
+**Unregister Account:**
+
+ .. code-block:: xml
+
+ <iq type="result">
+ <query xmlns="jabber:iq:register" />
+ </iq>
+
+**Incomplete Registration:**
+
+ .. code-block:: xml
+
+ <iq type="error">
+ <query xmlns="jabber:iq:register">
+ <username>Foo</username>
+ </query>
+ <error code="406" type="modify">
+ <not-acceptable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
+ </error>
+ </iq>
+
+**Conflicting Registrations:**
+
+ .. code-block:: xml
+
+ <iq type="error">
+ <query xmlns="jabber:iq:register">
+ <username>Foo</username>
+ <password>hunter2</password>
+ </query>
+ <error code="409" type="cancel">
+ <conflict xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
+ </error>
+ </iq>
+
+**Successful Registration:**
+
+ .. code-block:: xml
+
+ <iq type="result">
+ <query xmlns="jabber:iq:register" />
+ </iq>
+
+Cases 1 and 2: Registration Requests
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Responding to registration requests depends on if the requesting user already
+has an account. If there is an account, the response should include the
+``registered`` flag and the user's current registration information. Otherwise,
+we just send the fields for our registration form.
+
+We will handle both cases by creating a ``sendRegistrationForm`` method that
+will create either an empty of full form depending on if we provide it with
+user data. Since we need to know which form fields to include (especially if we
+add support for the other fields specified in XEP-0077), we will also create a
+method ``setForm`` which will take the names of the fields we wish to include.
+
+.. code-block:: python
+
+ def plugin_init(self):
+ self.description = "In-Band Registration"
+ self.xep = "0077"
+ self.form_fields = ('username', 'password')
+ ... remainder of plugin_init
+
+ ...
+
+ def __handleRegistration(self, iq):
+ if iq['type'] == 'get':
+ # Registration form requested
+ userData = self.backend[iq['from'].bare]
+ self.sendRegistrationForm(iq, userData)
+
+ def setForm(self, *fields):
+ self.form_fields = fields
+
+ def sendRegistrationForm(self, iq, userData=None):
+ reg = iq['register']
+ if userData is None:
+ userData = {}
+ else:
+ reg['registered'] = True
+
+ for field in self.form_fields:
+ data = userData.get(field, '')
+ if data:
+ # Add field with existing data
+ reg[field] = data
+ else:
+ # Add a blank field
+ reg.addField(field)
+
+ iq.reply().setPayload(reg.xml)
+ iq.send()
+
+Note how we are able to access our ``Registration`` stanza object with
+``iq['register']``.
+
+A User Backend
+++++++++++++++
+You might have noticed the reference to ``self.backend``, which is an object
+that abstracts away storing and retrieving user information. Since it is not
+much more than a dictionary, we will leave the implementation details to the
+final, full source code example.
+
+Case 3: Unregister an Account
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The next simplest case to consider is responding to a request to remove
+an account. If we receive a ``remove`` flag, we instruct the backend to
+remove the user's account. Since your application may need to know about
+when users are registered or unregistered, we trigger an event using
+``self.xmpp.event('unregister_user', iq)``. See the component examples below for
+how to respond to that event.
+
+.. code-block:: python
+
+ def __handleRegistration(self, iq):
+ if iq['type'] == 'get':
+ # Registration form requested
+ userData = self.backend[iq['from'].bare]
+ self.sendRegistrationForm(iq, userData)
+ elif iq['type'] == 'set':
+ # Remove an account
+ if iq['register']['remove']:
+ self.backend.unregister(iq['from'].bare)
+ self.xmpp.event('unregistered_user', iq)
+ iq.reply().send()
+ return
+
+Case 4: Incomplete Registration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+For the next case we need to check the user's registration to ensure it has all
+of the fields we wanted. The simple option that we will use is to loop over the
+field names and check each one; however, this means that all fields we send to
+the user are required. Adding optional fields is left to the reader.
+
+Since we have received an incomplete form, we need to send an error message back
+to the user. We have to send a few different types of errors, so we will also
+create a ``_sendError`` method that will add the appropriate ``error`` element
+to the IQ reply.
+
+.. code-block:: python
+
+ def __handleRegistration(self, iq):
+ if iq['type'] == 'get':
+ # Registration form requested
+ userData = self.backend[iq['from'].bare]
+ self.sendRegistrationForm(iq, userData)
+ elif iq['type'] == 'set':
+ if iq['register']['remove']:
+ # Remove an account
+ self.backend.unregister(iq['from'].bare)
+ self.xmpp.event('unregistered_user', iq)
+ iq.reply().send()
+ return
+
+ for field in self.form_fields:
+ if not iq['register'][field]:
+ # Incomplete Registration
+ self._sendError(iq, '406', 'modify', 'not-acceptable'
+ "Please fill in all fields.")
+ return
+
+ ...
+
+ def _sendError(self, iq, code, error_type, name, text=''):
+ iq.reply().setPayload(iq['register'].xml)
+ iq.error()
+ iq['error']['code'] = code
+ iq['error']['type'] = error_type
+ iq['error']['condition'] = name
+ iq['error']['text'] = text
+ iq.send()
+
+Cases 5 and 6: Conflicting and Successful Registration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We are down to the final decision on if we have a successful registration. We
+send the user's data to the backend with the ``self.backend.register`` method.
+If it returns ``True``, then registration has been successful. Otherwise,
+there has been a conflict with usernames and registration has failed. Like
+with unregistering an account, we trigger an event indicating that a user has
+been registered by using ``self.xmpp.event('registered_user', iq)``. See the
+component examples below for how to respond to this event.
+
+.. code-block:: python
+
+ def __handleRegistration(self, iq):
+ if iq['type'] == 'get':
+ # Registration form requested
+ userData = self.backend[iq['from'].bare]
+ self.sendRegistrationForm(iq, userData)
+ elif iq['type'] == 'set':
+ if iq['register']['remove']:
+ # Remove an account
+ self.backend.unregister(iq['from'].bare)
+ self.xmpp.event('unregistered_user', iq)
+ iq.reply().send()
+ return
+
+ for field in self.form_fields:
+ if not iq['register'][field]:
+ # Incomplete Registration
+ self._sendError(iq, '406', 'modify', 'not-acceptable',
+ "Please fill in all fields.")
+ return
+
+ if self.backend.register(iq['from'].bare, iq['register']):
+ # Successful registration
+ self.xmpp.event('registered_user', iq)
+ iq.reply().setPayload(iq['register'].xml)
+ iq.send()
+ else:
+ # Conflicting registration
+ self._sendError(iq, '409', 'cancel', 'conflict',
+ "That username is already taken.")
+
+Example Component Using the XEP-0077 Plugin
+-------------------------------------------
+Alright, the moment we've been working towards - actually using our plugin to
+simplify our other applications. Here is a basic component that simply manages
+user registrations and sends the user a welcoming message when they register,
+and a farewell message when they delete their account.
+
+Note that we have to register the ``'xep_0030'`` plugin first,
+and that we specified the form fields we wish to use with
+``self.xmpp.plugin['xep_0077'].setForm('username', 'password')``.
+
+.. code-block:: python
+
+ import sleekxmpp.componentxmpp
+
+ class Example(sleekxmpp.componentxmpp.ComponentXMPP):
+
+ def __init__(self, jid, password):
+ sleekxmpp.componentxmpp.ComponentXMPP.__init__(self, jid, password, 'localhost', 8888)
+
+ self.registerPlugin('xep_0030')
+ self.registerPlugin('xep_0077')
+ self.plugin['xep_0077'].setForm('username', 'password')
+
+ self.add_event_handler("registered_user", self.reg)
+ self.add_event_handler("unregistered_user", self.unreg)
+
+ def reg(self, iq):
+ msg = "Welcome! %s" % iq['register']['username']
+ self.sendMessage(iq['from'], msg, mfrom=self.fulljid)
+
+ def unreg(self, iq):
+ msg = "Bye! %s" % iq['register']['username']
+ self.sendMessage(iq['from'], msg, mfrom=self.fulljid)
+
+**Congratulations!** We now have a basic, functioning implementation of
+XEP-0077.
+
+Complete Source Code for XEP-0077 Plugin
+----------------------------------------
+Here is a copy of a more complete implementation of the plugin we created, but
+with some additional registration fields implemented.
+
+.. code-block:: python
+
+ """
+ Creating a SleekXMPP Plugin
+
+ This is a minimal implementation of XEP-0077 to serve
+ as a tutorial for creating SleekXMPP plugins.
+ """
+
+ from sleekxmpp.plugins.base import base_plugin
+ from sleekxmpp.xmlstream.handler.callback import Callback
+ from sleekxmpp.xmlstream.matcher.xpath import MatchXPath
+ from sleekxmpp.xmlstream import ElementBase, ET, JID, register_stanza_plugin
+ from sleekxmpp import Iq
+ import copy
+
+
+ class Registration(ElementBase):
+ namespace = 'jabber:iq:register'
+ name = 'query'
+ plugin_attrib = 'register'
+ interfaces = set(('username', 'password', 'email', 'nick', 'name',
+ 'first', 'last', 'address', 'city', 'state', 'zip',
+ 'phone', 'url', 'date', 'misc', 'text', 'key',
+ 'registered', 'remove', 'instructions'))
+ sub_interfaces = interfaces
+
+ def getRegistered(self):
+ present = self.xml.find('{%s}registered' % self.namespace)
+ return present is not None
+
+ def getRemove(self):
+ present = self.xml.find('{%s}remove' % self.namespace)
+ return present is not None
+
+ def setRegistered(self, registered):
+ if registered:
+ self.addField('registered')
+ else:
+ del self['registered']
+
+ def setRemove(self, remove):
+ if remove:
+ self.addField('remove')
+ else:
+ del self['remove']
+
+ def addField(self, name):
+ itemXML = ET.Element('{%s}%s' % (self.namespace, name))
+ self.xml.append(itemXML)
+
+
+ class UserStore(object):
+ def __init__(self):
+ self.users = {}
+
+ def __getitem__(self, jid):
+ return self.users.get(jid, None)
+
+ def register(self, jid, registration):
+ username = registration['username']
+
+ def filter_usernames(user):
+ return user != jid and self.users[user]['username'] == username
+
+ conflicts = filter(filter_usernames, self.users.keys())
+ if conflicts:
+ return False
+
+ self.users[jid] = registration
+ return True
+
+ def unregister(self, jid):
+ del self.users[jid]
+
+ class xep_0077(base_plugin):
+ """
+ XEP-0077 In-Band Registration
+ """
+
+ def plugin_init(self):
+ self.description = "In-Band Registration"
+ self.xep = "0077"
+ self.form_fields = ('username', 'password')
+ self.form_instructions = ""
+ self.backend = UserStore()
+
+ self.xmpp.registerHandler(
+ Callback('In-Band Registration',
+ MatchXPath('{%s}iq/{jabber:iq:register}query' % self.xmpp.default_ns),
+ self.__handleRegistration))
+ register_stanza_plugin(Iq, Registration)
+
+ def post_init(self):
+ base_plugin.post_init(self)
+ self.xmpp['xep_0030'].add_feature("jabber:iq:register")
+
+ def __handleRegistration(self, iq):
+ if iq['type'] == 'get':
+ # Registration form requested
+ userData = self.backend[iq['from'].bare]
+ self.sendRegistrationForm(iq, userData)
+ elif iq['type'] == 'set':
+ if iq['register']['remove']:
+ # Remove an account
+ self.backend.unregister(iq['from'].bare)
+ self.xmpp.event('unregistered_user', iq)
+ iq.reply().send()
+ return
+
+ for field in self.form_fields:
+ if not iq['register'][field]:
+ # Incomplete Registration
+ self._sendError(iq, '406', 'modify', 'not-acceptable',
+ "Please fill in all fields.")
+ return
+
+ if self.backend.register(iq['from'].bare, iq['register']):
+ # Successful registration
+ self.xmpp.event('registered_user', iq)
+ iq.reply().setPayload(iq['register'].xml)
+ iq.send()
+ else:
+ # Conflicting registration
+ self._sendError(iq, '409', 'cancel', 'conflict',
+ "That username is already taken.")
+
+ def setForm(self, *fields):
+ self.form_fields = fields
+
+ def setInstructions(self, instructions):
+ self.form_instructions = instructions
+
+ def sendRegistrationForm(self, iq, userData=None):
+ reg = iq['register']
+ if userData is None:
+ userData = {}
+ else:
+ reg['registered'] = True
+
+ if self.form_instructions:
+ reg['instructions'] = self.form_instructions
+
+ for field in self.form_fields:
+ data = userData.get(field, '')
+ if data:
+ # Add field with existing data
+ reg[field] = data
+ else:
+ # Add a blank field
+ reg.addField(field)
+
+ iq.reply().setPayload(reg.xml)
+ iq.send()
+
+ def _sendError(self, iq, code, error_type, name, text=''):
+ iq.reply().setPayload(iq['register'].xml)
+ iq.error()
+ iq['error']['code'] = code
+ iq['error']['type'] = error_type
+ iq['error']['condition'] = name
+ iq['error']['text'] = text
+ iq.send()
diff --git a/docs/event_index.rst b/docs/event_index.rst
new file mode 100644
index 00000000..2c5dfd39
--- /dev/null
+++ b/docs/event_index.rst
@@ -0,0 +1,271 @@
+Event Index
+===========
+
+.. glossary::
+ :sorted:
+
+ connected
+ - **Data:** ``{}``
+ - **Source:** :py:class:`~sleekxmpp.clientxmpp.ClientXMPP`
+
+ Signal that a connection has been made with the XMPP server, but a session
+ has not yet been established.
+
+ changed_status
+ - **Data:** :py:class:`~sleekxmpp.Presence`
+ - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
+
+ Triggered when a presence stanza is received from a JID with a show type
+ different than the last presence stanza from the same JID.
+
+ changed_subscription
+ - **Data:** :py:class:`~sleekxmpp.Presence`
+ - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
+
+ Triggered whenever a presence stanza with a type of ``subscribe``,
+ ``subscribed``, ``unsubscribe``, or ``unsubscribed`` is received.
+
+ Note that if the values ``xmpp.auto_authorize`` and ``xmpp.auto_subscribe``
+ are set to ``True`` or ``False``, and not ``None``, then SleekXMPP will
+ either accept or reject all subscription requests before your event handlers
+ are called. Set these values to ``None`` if you wish to make more complex
+ subscription decisions.
+
+ chatstate_active
+ - **Data:**
+ - **Source:**
+
+ chatstate_composing
+ - **Data:**
+ - **Source:**
+
+ chatstate_gone
+ - **Data:**
+ - **Source:**
+
+ chatstate_inactive
+ - **Data:**
+ - **Source:**
+
+ chatstate_paused
+ - **Data:**
+ - **Source:**
+
+ disco_info
+ - **Data:** :py:class:`~sleekxmpp.plugins.xep_0030.stanza.DiscoInfo`
+ - **Source:** :py:class:`~sleekxmpp.plugins.xep_0030.disco.xep_0030`
+
+ Triggered whenever a ``disco#info`` result stanza is received.
+
+ disco_items
+ - **Data:** :py:class:`~sleekxmpp.plugins.xep_0030.stanza.DiscoItems`
+ - **Source:** :py:class:`~sleekxmpp.plugins.xep_0030.disco.xep_0030`
+
+ Triggered whenever a ``disco#items`` result stanza is received.
+
+ disconnected
+ - **Data:** ``{}``
+ - **Source:** :py:class:`~sleekxmpp.ClientXMPP`
+
+ Signal that the connection with the XMPP server has been lost.
+
+ entity_time
+ - **Data:**
+ - **Source:**
+
+ failed_auth
+ - **Data:** ``{}``
+ - **Source:** :py:class:`~sleekxmpp.ClientXMPP`, :py:class:`~sleekxmpp.plugins.xep_0078.xep_0078`
+
+ Signal that the server has rejected the provided login credentials.
+
+ gmail_notify
+ - **Data:** ``{}``
+ - **Source:** :py:class:`~sleekxmpp.plugins.gmail_notify.gmail_notify`
+
+ Signal that there are unread emails for the Gmail account associated with the current XMPP account.
+
+ gmail_messages
+ - **Data:** :py:class:`~sleekxmpp.Iq`
+ - **Source:** :py:class:`~sleekxmpp.plugins.gmail_notify.gmail_notify`
+
+ Signal that there are unread emails for the Gmail account associated with the current XMPP account.
+
+ got_online
+ - **Data:** :py:class:`~sleekxmpp.Presence`
+ - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
+
+ If a presence stanza is received from a JID which was previously marked as
+ offline, and the presence has a show type of '``chat``', '``dnd``', '``away``',
+ or '``xa``', then this event is triggered as well.
+
+ got_offline
+ - **Data:** :py:class:`~sleekxmpp.Presence`
+ - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
+
+ Signal that an unavailable presence stanza has been received from a JID.
+
+ groupchat_invite
+ - **Data:**
+ - **Source:**
+
+ groupchat_direct_invite
+ - **Data:** :py:class:`~sleekxmpp.Message`
+ - **Source:** :py:class:`~sleekxmpp.plugins.xep_0249.direct`
+
+ groupchat_message
+ - **Data:** :py:class:`~sleekxmpp.Message`
+ - **Source:** :py:class:`~sleekxmpp.plugins.xep_0045.xep_0045`
+
+ Triggered whenever a message is received from a multi-user chat room.
+
+ groupchat_presence
+ - **Data:** :py:class:`~sleekxmpp.Presence`
+ - **Source:** :py:class:`~sleekxmpp.plugins.xep_0045.xep_0045`
+
+ Triggered whenever a presence stanza is received from a user in a multi-user chat room.
+
+ groupchat_subject
+ - **Data:** :py:class:`~sleekxmpp.Message`
+ - **Source:** :py:class:`~sleekxmpp.plugins.xep_0045.xep_0045`
+
+ Triggered whenever the subject of a multi-user chat room is changed, or announced when joining a room.
+
+ killed
+ - **Data:**
+ - **Source:**
+
+ last_activity
+ - **Data:**
+ - **Source:**
+
+ message
+ - **Data:** :py:class:`~sleekxmpp.Message`
+ - **Source:** :py:class:`BaseXMPP <sleekxmpp.BaseXMPP>`
+
+ Makes the contents of message stanzas available whenever one is received. Be
+ sure to check the message type in order to handle error messages.
+
+ message_form
+ - **Data:** :py:class:`~sleekxmpp.plugins.xep_0004.Form`
+ - **Source:** :py:class:`~sleekxmpp.plugins.xep_0004.xep_0004`
+
+ Currently the same as :term:`message_xform`.
+
+ message_xform
+ - **Data:** :py:class:`~sleekxmpp.plugins.xep_0004.Form`
+ - **Source:** :py:class:`~sleekxmpp.plugins.xep_0004.xep_0004`
+
+ Triggered whenever a data form is received inside a message.
+
+ mucc::[room]::got_offline
+ - **Data:**
+ - **Source:**
+
+ muc::[room]::got_online
+ - **Data:**
+ - **Source:**
+
+ muc::[room]::message
+ - **Data:**
+ - **Source:**
+
+ muc::[room]::presence
+ - **Data:**
+ - **Source:**
+
+ presence_available
+ - **Data:** :py:class:`~sleekxmpp.Presence`
+ - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
+
+ A presence stanza with a type of '``available``' is received.
+
+ presence_error
+ - **Data:** :py:class:`~sleekxmpp.Presence`
+ - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
+
+ A presence stanza with a type of '``error``' is received.
+
+ presence_form
+ - **Data:** :py:class:`~sleekxmpp.plugins.xep_0004.Form`
+ - **Source:** :py:class:`~sleekxmpp.plugins.xep_0004.xep_0004`
+
+ This event is present in the XEP-0004 plugin code, but is currently not used.
+
+ presence_probe
+ - **Data:** :py:class:`~sleekxmpp.Presence`
+ - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
+
+ A presence stanza with a type of '``probe``' is received.
+
+ presence_subscribe
+ - **Data:** :py:class:`~sleekxmpp.Presence`
+ - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
+
+ A presence stanza with a type of '``subscribe``' is received.
+
+ presence_subscribed
+ - **Data:** :py:class:`~sleekxmpp.Presence`
+ - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
+
+ A presence stanza with a type of '``subscribed``' is received.
+
+ presence_unavailable
+ - **Data:** :py:class:`~sleekxmpp.Presence`
+ - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
+
+ A presence stanza with a type of '``unavailable``' is received.
+
+ presence_unsubscribe
+ - **Data:** :py:class:`~sleekxmpp.Presence`
+ - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
+
+ A presence stanza with a type of '``unsubscribe``' is received.
+
+ presence_unsubscribed
+ - **Data:** :py:class:`~sleekxmpp.Presence`
+ - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
+
+ A presence stanza with a type of '``unsubscribed``' is received.
+
+ roster_update
+ - **Data:** :py:class:`~sleekxmpp.stanza.Roster`
+ - **Source:** :py:class:`~sleekxmpp.ClientXMPP`
+
+ An IQ result containing roster entries is received.
+
+ sent_presence
+ - **Data:** ``{}``
+ - **Source:** :py:class:`BaseXMPP <sleekxmpp.BaseXMPP>`
+
+ Signal that an initial presence stanza has been written to the XML stream.
+
+ session_end
+ - **Data:** ``{}``
+ - **Source:** :py:class:`ClientXMPP <sleekxmpp.ClientXMPP>`,
+ :py:class:`ComponentXMPP <sleekxmpp.ComponentXMPP>`
+ :py:class:`XEP-0078 <sleekxmpp.plugins.xep_0078>`
+
+ Signal that a connection to the XMPP server has been lost and the current
+ stream session has ended. Currently equivalent to :term:`disconnected`, but
+ future implementation of `XEP-0198: Stream Management <http://xmpp.org/extensions/xep-0198.html>`_
+ will distinguish the two events.
+
+ Plugins that maintain session-based state should clear themselves when
+ this event is fired.
+
+ session_start
+ - **Data:** ``{}``
+ - **Source:** :py:class:`ClientXMPP <sleekxmpp.ClientXMPP>`,
+ :py:class:`ComponentXMPP <sleekxmpp.ComponentXMPP>`
+ :py:class:`XEP-0078 <sleekxmpp.plugins.xep_0078>`
+
+ Signal that a connection to the XMPP server has been made and a session has been established.
+
+ socket_error
+ - **Data:** ``Socket`` exception object
+ - **Source:** :py:class:`~sleekxmpp.xmlstream.XMLstream`
+
+ stream_error
+ - **Data:** :py:class:`~sleekxmpp.stanza.StreamError`
+ - **Source:** :py:class:`~sleekxmpp.BaseXMPP`
diff --git a/docs/features.rst b/docs/features.rst
new file mode 100644
index 00000000..4d93d5c3
--- /dev/null
+++ b/docs/features.rst
@@ -0,0 +1,2 @@
+How to Use Stream Features
+==========================
diff --git a/docs/getting_started/component.rst b/docs/getting_started/component.rst
new file mode 100644
index 00000000..ce548ba4
--- /dev/null
+++ b/docs/getting_started/component.rst
@@ -0,0 +1,75 @@
+.. _echocomponent:
+
+=================================
+Create and Run a Server Component
+=================================
+
+.. note::
+
+ If you have any issues working through this quickstart guide
+ or the other tutorials here, please either send a message to the
+ `mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
+ or join the chat room at `sleek@conference.jabber.org
+ <xmpp:sleek@conference.jabber.org?join>`_.
+
+If you have not yet installed SleekXMPP, do so now by either checking out a version
+from `Github <http://github.com/fritzy/SleekXMPP>`_, or installing it using ``pip``
+or ``easy_install``.
+
+.. code-block:: sh
+
+ pip install sleekxmpp # Or: easy_install sleekxmpp
+
+
+Many XMPP applications eventually graduate to requiring to run as a server
+component in order to meet scalability requirements. To demonstrate how to
+turn an XMPP client bot into a component, we'll turn the echobot example
+(:ref:`echobot`) into a component version.
+
+The first difference is that we will add an additional import statement:
+
+.. code-block:: python
+
+ from sleekxmpp.componentxmpp import ComponentXMPP
+
+Likewise, we will change the bot's class definition to match:
+
+.. code-block:: python
+
+ class EchoComponent(ComponentXMPP):
+
+ def __init__(self, jid, secret, server, port):
+ ComponentXMPP.__init__(self, jid, secret, server, port)
+
+A component instance requires two extra parameters compared to a client
+instance: ``server`` and ``port``. These specifiy the name and port of
+the XMPP server that will be accepting the component. For example, for
+a MUC component, the following could be used:
+
+.. code-block:: python
+
+ muc = ComponentXMPP('muc.sleekxmpp.com', '******', 'sleekxmpp.com', 5555)
+
+.. note::
+
+ The ``server`` value is **NOT** derived from the provided JID for the
+ component, unlike with client connections.
+
+One difference with the component version is that we do not have
+to handle the :term:`session_start` event if we don't wish to deal
+with presence.
+
+The other, main difference with components is that the
+``'from'`` value for every stanza must be explicitly set, since
+components may send stanzas from multiple JIDs. To do so,
+the :meth:`~sleekxmpp.basexmpp.BaseXMPP.send_message()` and
+:meth:`~sleekxmpp.basexmpp.BaseXMPP.send_presence()` accept the parameters
+``mfrom`` and ``pfrom``, respectively. For any method that uses
+:class:`~sleekxmpp.stanza.iq.Iq` stanzas, ``ifrom`` may be used.
+
+
+Final Product
+-------------
+
+.. include:: ../../examples/echo_component.py
+ :literal:
diff --git a/docs/getting_started/echobot.rst b/docs/getting_started/echobot.rst
new file mode 100644
index 00000000..053a76f2
--- /dev/null
+++ b/docs/getting_started/echobot.rst
@@ -0,0 +1,390 @@
+.. _echobot:
+
+===============================
+SleekXMPP Quickstart - Echo Bot
+===============================
+
+.. note::
+
+ If you have any issues working through this quickstart guide
+ or the other tutorials here, please either send a message to the
+ `mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
+ or join the chat room at `sleek@conference.jabber.org
+ <xmpp:sleek@conference.jabber.org?join>`_.
+
+If you have not yet installed SleekXMPP, do so now by either checking out a version
+from `Github <http://github.com/fritzy/SleekXMPP>`_, or installing it using ``pip``
+or ``easy_install``.
+
+.. code-block:: sh
+
+ pip install sleekxmpp # Or: easy_install sleekxmpp
+
+
+As a basic starting project, we will create an echo bot which will reply to any
+messages sent to it. We will also go through adding some basic command line configuration
+for enabling or disabling debug log outputs and setting the username and password
+for the bot.
+
+For the command line options processing, we will use the built-in ``optparse``
+module and the ``getpass`` module for reading in passwords.
+
+TL;DR Just Give Me the Code
+---------------------------
+As you wish: :ref:`the completed example <echobot_complete>`.
+
+Overview
+--------
+
+To get started, here is a brief outline of the structure that the final project will have:
+
+.. code-block:: python
+
+ #!/usr/bin/env python
+ # -*- coding: utf-8 -*-
+
+ import sys
+ import logging
+ import getpass
+ from optparse import OptionParser
+
+ import sleekxmpp
+
+ '''Here we will create out echo bot class'''
+
+ if __name__ == '__main__':
+ '''Here we will configure and read command line options'''
+
+ '''Here we will instantiate our echo bot'''
+
+ '''Finally, we connect the bot and start listening for messages'''
+
+Default Encoding
+----------------
+XMPP requires support for UTF-8 and so SleekXMPP must use UTF-8 as well. In
+Python3 this is simple because Unicode is the default string type. For Python2.6+
+the situation is not as easy because standard strings are simply byte arrays and
+use ASCII. We can get Python to use UTF-8 as the default encoding by including:
+
+.. code-block:: python
+
+ if sys.version_info < (3, 0):
+ reload(sys)
+ sys.setdefaultencoding('utf8')
+
+.. warning::
+
+ Until we are able to ensure that SleekXMPP will always use Unicode in Python2.6+, this
+ may cause issues embedding SleekXMPP into other applications which assume ASCII encoding.
+
+Creating the EchoBot Class
+--------------------------
+
+There are three main types of entities within XMPP — servers, components, and
+clients. Since our echo bot will only be responding to a few people, and won't need
+to remember thousands of users, we will use a client connection. A client connection
+is the same type that you use with your standard IM client such as Pidgin or Psi.
+
+SleekXMPP comes with a :class:`ClientXMPP <sleekxmpp.clientxmpp.ClientXMPP>` class
+which we can extend to add our message echoing feature. :class:`ClientXMPP <sleekxmpp.clientxmpp.ClientXMPP>`
+requires the parameters ``jid`` and ``password``, so we will let our ``EchoBot`` class accept those
+as well.
+
+.. code-block:: python
+
+ class EchoBot(sleekxmpp.ClientXMPP):
+
+ def __init__(self, jid, password):
+ super(EchoBot, self).__init__(jid, password)
+
+Handling Session Start
+~~~~~~~~~~~~~~~~~~~~~~
+The XMPP spec requires clients to broadcast its presence and retrieve its roster (buddy list) once
+it connects and establishes a session with the XMPP server. Until these two tasks are completed,
+some servers may not deliver or send messages or presence notifications to the client. So we now
+need to be sure that we retrieve our roster and send an initial presence once the session has
+started. To do that, we will register an event handler for the :term:`session_start` event.
+
+.. code-block:: python
+
+ def __init__(self, jid, password):
+ super(EchoBot, self).__init__(jid, password)
+
+ self.add_event_handler('session_start', self.start)
+
+
+Since we want the method ``self.start`` to execute when the :term:`session_start` event is triggered,
+we also need to define the ``self.start`` handler.
+
+.. code-block:: python
+
+ def start(self, event):
+ self.send_presence()
+ self.get_roster()
+
+.. warning::
+
+ Not sending an initial presence and retrieving the roster when using a client instance can
+ prevent your program from receiving presence notifications or messages depending on the
+ XMPP server you have chosen.
+
+Our event handler, like every event handler, accepts a single parameter which typically is the stanza
+that was received that caused the event. In this case, ``event`` will just be an empty dictionary since
+there is no associated data.
+
+Our first task of sending an initial presence is done using :meth:`send_presence <sleekxmpp.basexmpp.BaseXMPP.send_presence>`.
+Calling :meth:`send_presence <sleekxmpp.basexmpp.BaseXMPP.send_presence>` without any arguments will send the simplest
+stanza allowed in XMPP:
+
+.. code-block:: xml
+
+ <presence />
+
+
+The second requirement is fulfilled using :meth:`get_roster <sleekxmpp.clientxmpp.ClientXMPP.get_roster>`, which
+will send an IQ stanza requesting the roster to the server and then wait for the response. You may be wondering
+what :meth:`get_roster <sleekxmpp.clientxmpp.ClientXMPP.get_roster>` returns since we are not saving any return
+value. The roster data is saved by an internal handler to ``self.roster``, and in the case of a :class:`ClientXMPP
+<sleekxmpp.clientxmpp.ClientXMPP>` instance to ``self.client_roster``. (The difference between ``self.roster`` and
+``self.client_roster`` is that ``self.roster`` supports storing roster information for multiple JIDs, which is useful
+for components, whereas ``self.client_roster`` stores roster data for just the client's JID.)
+
+It is possible for a timeout to occur while waiting for the server to respond, which can happen if the
+network is excessively slow or the server is no longer responding. In that case, an :class:`IQTimeout
+<sleekxmpp.exceptions.IQTimeout>` is raised. Similarly, an :class:`IQError <sleekxmpp.exceptions.IQError>` exception can
+be raised if the request contained bad data or requested the roster for the wrong user. In either case, you can wrap the
+``get_roster()`` call in a ``try``/``except`` block to retry the roster retrieval process.
+
+The XMPP stanzas from the roster retrieval process could look like this:
+
+.. code-block:: xml
+
+ <iq type="get">
+ <query xmlns="jabber:iq:roster" />
+ </iq>
+
+ <iq type="result" to="echobot@example.com" from="example.com">
+ <query xmlns="jabber:iq:roster">
+ <item jid="friend@example.com" subscription="both" />
+ </query>
+ </iq>
+
+Responding to Messages
+~~~~~~~~~~~~~~~~~~~~~~
+Now that an ``EchoBot`` instance handles :term:`session_start`, we can begin receiving and
+responding to messages. Now we can register a handler for the :term:`message` event that is raised
+whenever a messsage is received.
+
+.. code-block:: python
+
+ def __init__(self, jid, password):
+ super(EchoBot, self).__init__(jid, password)
+
+ self.add_event_handler('session_start', self.start)
+ self.add_event_handler('message', self.message)
+
+
+The :term:`message` event is fired whenever a ``<message />`` stanza is received, including for
+group chat messages, errors, etc. Properly responding to messages thus requires checking the
+``'type'`` interface of the message :term:`stanza object`. For responding to only messages
+addressed to our bot (and not from a chat room), we check that the type is either ``normal``
+or ``chat``. (Other potential types are ``error``, ``headline``, and ``groupchat``.)
+
+.. code-block:: python
+
+ def message(self, msg):
+ if msg['type'] in ('normal', 'chat'):
+ msg.reply("Thanks for sending:\n%s" % msg['body']).send()
+
+Let's take a closer look at the ``.reply()`` method used above. For message stanzas,
+``.reply()`` accepts the parameter ``body`` (also as the first positional argument),
+which is then used as the value of the ``<body />`` element of the message.
+Setting the appropriate ``to`` JID is also handled by ``.reply()``.
+
+Another way to have sent the reply message would be to use :meth:`send_message <sleekxmpp.basexmpp.BaseXMPP.send_message>`,
+which is a convenience method for generating and sending a message based on the values passed to it. If we were to use
+this method, the above code would look as so:
+
+.. code-block:: python
+
+ def message(self, msg):
+ if msg['type'] in ('normal', 'chat'):
+ self.send_message(mto=msg['from'],
+ mbody='Thanks for sending:\n%s' % msg['body'])
+
+Whichever method you choose to use, the results in action will look like this:
+
+.. code-block:: xml
+
+ <message to="echobot@example.com" from="someuser@example.net" type="chat">
+ <body>Hej!</body>
+ </message>
+
+ <message to="someuser@example.net" type="chat">
+ <body>Thanks for sending:
+ Hej!</body>
+ </message>
+
+.. note::
+ XMPP does not require stanzas sent by a client to include a ``from`` attribute, and
+ leaves that responsibility to the XMPP server. However, if a sent stanza does
+ include a ``from`` attribute, it must match the full JID of the client or some
+ servers will reject it. SleekXMPP thus leaves out the ``from`` attribute when replying
+ using a client connection.
+
+Command Line Arguments and Logging
+----------------------------------
+
+While this isn't part of SleekXMPP itself, we do want our echo bot program to be able
+to accept a JID and password from the command line instead of hard coding them. We will
+use the ``optparse`` module for this, though there are several alternative methods, including
+the newer ``argparse`` module.
+
+We want to accept three parameters: the JID for the echo bot, its password, and a flag for
+displaying the debugging logs. We also want these to be optional parameters, since passing
+a password directly through the command line can be a security risk.
+
+.. code-block:: python
+
+ if __name__ == '__main__':
+ optp = OptionParser()
+
+ optp.add_option('-d', '--debug', help='set logging to DEBUG',
+ action='store_const', dest='loglevel',
+ const=logging.DEBUG, default=logging.INFO)
+ optp.add_option("-j", "--jid", dest="jid",
+ help="JID to use")
+ optp.add_option("-p", "--password", dest="password",
+ help="password to use")
+
+ opts, args = optp.parse_args()
+
+ if opts.jid is None:
+ opts.jid = raw_input("Username: ")
+ if opts.password is None:
+ opts.password = getpass.getpass("Password: ")
+
+Since we included a flag for enabling debugging logs, we need to configure the
+``logging`` module to behave accordingly.
+
+.. code-block:: python
+
+ if __name__ == '__main__':
+
+ # .. option parsing from above ..
+
+ logging.basicConfig(level=opts.loglevel,
+ format='%(levelname)-8s %(message)s')
+
+
+Connecting to the Server and Processing
+---------------------------------------
+There are three steps remaining until our echo bot is complete:
+ 1. We need to instantiate the bot.
+ 2. The bot needs to connect to an XMPP server.
+ 3. We have to instruct the bot to start running and processing messages.
+
+Creating the bot is straightforward, but we can also perform some configuration
+at this stage. For example, let's say we want our bot to support `service discovery
+<http://xmpp.org/extensions/xep-0030.html>`_ and `pings <http://xmpp.org/extensions/xep-0199.html>`_:
+
+.. code-block:: python
+
+ if __name__ == '__main__':
+
+ # .. option parsing and logging steps from above
+
+ xmpp = EchoBot(opts.jid, opts.password)
+ xmpp.register_plugin('xep_0030') # Service Discovery
+ xmpp.register_plugin('xep_0199') # Ping
+
+If the ``EchoBot`` class had a hard dependency on a plugin, we could register that plugin in
+the ``EchoBot.__init__`` method instead.
+
+.. note::
+
+ If you are using the OpenFire server, you will need to include an additional
+ configuration step. OpenFire supports a different version of SSL than what
+ most servers and SleekXMPP support.
+
+ .. code-block:: python
+
+ import ssl
+ xmpp.ssl_version = ssl.PROTOCOL_SSLv3
+
+Now we're ready to connect and begin echoing messages. If you have the package
+``dnspython`` installed, then the :meth:`sleekxmpp.clientxmpp.ClientXMPP` method
+will perform a DNS query to find the appropriate server to connect to for the
+given JID. If you do not have ``dnspython``, then SleekXMPP will attempt to
+connect to the hostname used by the JID, unless an address tuple is supplied
+to :meth:`sleekxmpp.clientxmpp.ClientXMPP`.
+
+.. code-block:: python
+
+ if __name__ == '__main__':
+
+ # .. option parsing & echo bot configuration
+
+ if xmpp.connect():
+ xmpp.process(block=True)
+ else:
+ print('Unable to connect')
+
+.. note::
+
+ For Google Talk users withouth ``dnspython`` installed, the above code
+ should look like:
+
+ .. code-block:: python
+
+ if __name__ == '__main__':
+
+ # .. option parsing & echo bot configuration
+
+ if xmpp.connect(('talk.google.com', 5222)):
+ xmpp.process(block=True)
+ else:
+ print('Unable to connect')
+
+To begin responding to messages, you'll see we called :meth:`sleekxmpp.basexmpp.BaseXMPP.process`
+which will start the event handling, send queue, and XML reader threads. It will also call
+the :meth:`sleekxmpp.plugins.base.base_plugin.post_init` method on all registered plugins. By
+passing ``block=True`` to :meth:`sleekxmpp.basexmpp.BaseXMPP.process` we are running the
+main processing loop in the main thread of execution. The :meth:`sleekxmpp.basexmpp.BaseXMPP.process`
+call will not return until after SleekXMPP disconnects. If you need to run the client in the background
+for another program, use ``block=False`` to spawn the processing loop in its own thread.
+
+.. note::
+
+ Before 1.0, controlling the blocking behaviour of :meth:`sleekxmpp.basexmpp.BaseXMPP.process` was
+ done via the ``threaded`` argument. This arrangement was a source of confusion because some users
+ interpreted that as controlling whether or not SleekXMPP used threads at all, instead of how
+ the processing loop itself was spawned.
+
+ The statements ``xmpp.process(threaded=False)`` and ``xmpp.process(block=True)`` are equivalent.
+
+
+.. _echobot_complete:
+
+The Final Product
+-----------------
+
+Here then is what the final result should look like after working through the guide above. The code
+can also be found in the SleekXMPP `examples directory <http://github.com/fritzy/SleekXMPP/tree/master/examples>`_.
+
+.. compound::
+
+ You can run the code using:
+
+ .. code-block:: sh
+
+ python echobot.py -d -j echobot@example.com
+
+ which will prompt for the password and then begin echoing messages. To test, open
+ your regular IM client and start a chat with the echo bot. Messages you send to it should
+ be mirrored back to you. Be careful if you are using the same JID for the echo bot that
+ you also have logged in with another IM client. Messages could be routed to your IM client instead
+ of the bot.
+
+.. include:: ../../examples/echo_client.py
+ :literal:
diff --git a/docs/getting_started/iq.rst b/docs/getting_started/iq.rst
new file mode 100644
index 00000000..98e0bdaf
--- /dev/null
+++ b/docs/getting_started/iq.rst
@@ -0,0 +1,182 @@
+Send/Receive IQ Stanzas
+=======================
+
+Unlike :class:`~sleekxmpp.stanza.message.Message` and
+:class:`~sleekxmpp.stanza.presence.Presence` stanzas which only use
+text data for basic usage, :class:`~sleekxmpp.stanza.iq.Iq` stanzas
+require using XML payloads, and generally entail creating a new
+SleekXMPP plugin to provide the necessary convenience methods to
+make working with them easier.
+
+Basic Use
+---------
+
+XMPP's use of :class:`~sleekxmpp.stanza.iq.Iq` stanzas is built around
+namespaced ``<query />`` elements. For clients, just sending the
+empty ``<query />`` element will suffice for retrieving information. For
+example, a very basic implementation of service discovery would just
+need to be able to send:
+
+.. code-block:: xml
+
+ <iq to="user@example.com" type="get" id="1">
+ <query xmlns="http://jabber.org/protocol/disco#info" />
+ </iq>
+
+Creating Iq Stanzas
+~~~~~~~~~~~~~~~~~~~
+
+SleekXMPP provides built-in support for creating basic :class:`~sleekxmpp.stanza.iq.Iq`
+stanzas this way. The relevant methods are:
+
+* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq`
+* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_get`
+* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_set`
+* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_result`
+* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_error`
+* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_query`
+
+These methods all follow the same pattern: create or modify an existing
+:class:`~sleekxmpp.stanza.iq.Iq` stanza, set the ``'type'`` value based
+on the method name, and finally add a ``<query />`` element with the given
+namespace. For example, to produce the query above, you would use:
+
+.. code-block:: python
+
+ self.make_iq_get(queryxmlns='http://jabber.org/protocol/disco#info',
+ ito='user@example.com')
+
+
+Sending Iq Stanzas
+~~~~~~~~~~~~~~~~~~
+
+Once an :class:`~sleekxmpp.stanza.iq.Iq` stanza is created, sending it
+over the wire is done using its :meth:`~sleekxmpp.stanza.iq.Iq.send()`
+method, like any other stanza object. However, there are a few extra
+options to control how to wait for the query's response.
+
+These options are:
+
+* ``block``: The default behaviour is that :meth:`~sleekxmpp.stanza.iq.Iq.send()`
+ will block until a response is received and the response stanza will be the
+ return value. Setting ``block`` to ``False`` will cause the call to return
+ immediately. In which case, you will need to arrange some way to capture
+ the response stanza if you need it.
+
+* ``timeout``: When using the blocking behaviour, the call will eventually
+ timeout with an error. The default timeout is 30 seconds, but this may
+ be overidden two ways. To change the timeout globally, set:
+
+ .. code-block:: python
+
+ self.response_timeout = 10
+
+ To change the timeout for a single call, the ``timeout`` parameter works:
+
+ .. code-block:: python
+
+ iq.send(timeout=60)
+
+* ``callback``: When not using a blocking call, using the ``callback``
+ argument is a simple way to register a handler that will execute
+ whenever a response is finally received. Using this method, there
+ is no timeout limit. In case you need to remove the callback, the
+ name of the newly created callback is returned.
+
+ .. code-block:: python
+
+ cb_name = iq.send(callback=self.a_callback)
+
+ # ... later if we need to cancel
+ self.remove_handler(cb_name)
+
+Properly working with :class:`~sleekxmpp.stanza.iq.Iq` stanzas requires
+handling the intended, normal flow, error responses, and timed out
+requests. To make this easier, two exceptions may be thrown by
+:meth:`~sleekxmpp.stanza.iq.Iq.send()`: :exc:`~sleekxmpp.exceptions.IqError`
+and :exc:`~sleekxmpp.exceptions.IqTimeout`. These exceptions only
+apply to the default, blocking calls.
+
+.. code-block:: python
+
+ try:
+ resp = iq.send()
+ # ... do stuff with expected Iq result
+ except IqError as e:
+ err_resp = e.iq
+ # ... handle error case
+ except IqTimeout:
+ # ... no response received in time
+ pass
+
+If you do not care to distinguish between errors and timeouts, then you
+can combine both cases with a generic :exc:`~sleekxmpp.exceptions.XMPPError`
+exception:
+
+.. code-block:: python
+
+ try:
+ resp = iq.send()
+ except XMPPError:
+ # ... Don't care about the response
+ pass
+
+Advanced Use
+------------
+
+Going beyond the basics provided by SleekXMPP requires building at least a
+rudimentary SleekXMPP plugin to create a :term:`stanza object` for
+interfacting with the :class:`~sleekxmpp.stanza.iq.Iq` payload.
+
+.. seealso::
+
+ * :ref:`create-plugin`
+ * :ref:`work-with-stanzas`
+ * :ref:`using-handlers-matchers`
+
+
+The typical way to respond to :class:`~sleekxmpp.stanza.iq.Iq` requests is
+to register stream handlers. As an example, suppose we create a stanza class
+named ``CustomXEP`` which uses the XML element ``<query xmlns="custom-xep" />``,
+and has a :attr:`~sleekxmpp.xmlstream.stanzabase.ElementBase.plugin_attrib` value
+of ``custom_xep``.
+
+There are two types of incoming :class:`~sleekxmpp.stanza.iq.Iq` requests:
+``get`` and ``set``. You can register a handler that will accept both and then
+filter by type as needed, as so:
+
+.. code-block:: python
+
+ self.register_handler(Callback(
+ 'CustomXEP Handler',
+ StanzaPath('iq/custom_xep'),
+ self._handle_custom_iq))
+
+ # ...
+
+ def _handle_custom_iq(self, iq):
+ if iq['type'] == 'get':
+ # ...
+ pass
+ elif iq['type'] == 'set':
+ # ...
+ pass
+ else:
+ # ... This will capture error responses too
+ pass
+
+If you want to filter out query types beforehand, you can adjust the matching
+filter by using ``@type=get`` or ``@type=set`` if you are using the recommended
+:class:`~sleekxmpp.xmlstream.matcher.stanzapath.StanzaPath` matcher.
+
+.. code-block:: python
+
+ self.register_handler(Callback(
+ 'CustomXEP Handler',
+ StanzaPath('iq@type=get/custom_xep'),
+ self._handle_custom_iq_get))
+
+ # ...
+
+ def _handle_custom_iq_get(self, iq):
+ assert(iq['type'] == 'get')
diff --git a/docs/getting_started/muc.rst b/docs/getting_started/muc.rst
new file mode 100644
index 00000000..08f721f7
--- /dev/null
+++ b/docs/getting_started/muc.rst
@@ -0,0 +1,2 @@
+Mulit-User Chat (MUC) Bot
+=========================
diff --git a/docs/getting_started/presence.rst b/docs/getting_started/presence.rst
new file mode 100644
index 00000000..e070e812
--- /dev/null
+++ b/docs/getting_started/presence.rst
@@ -0,0 +1,2 @@
+Manage Presence Subscriptions
+=============================
diff --git a/docs/getting_started/proxy.rst b/docs/getting_started/proxy.rst
new file mode 100644
index 00000000..60d521c5
--- /dev/null
+++ b/docs/getting_started/proxy.rst
@@ -0,0 +1,42 @@
+.. _proxy:
+
+=========================
+Enable HTTP Proxy Support
+=========================
+
+.. note::
+
+ If you have any issues working through this quickstart guide
+ or the other tutorials here, please either send a message to the
+ `mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
+ or join the chat room at `sleek@conference.jabber.org
+ <xmpp:sleek@conference.jabber.org?join>`_.
+
+In some instances, you may wish to route XMPP traffic through
+an HTTP proxy, probably to get around restrictive firewalls.
+SleekXMPP provides support for basic HTTP proxying with DIGEST
+authentication.
+
+Enabling proxy support is done in two steps. The first is to instruct SleekXMPP
+to use a proxy, and the second is to configure the proxy details:
+
+.. code-block:: python
+
+ xmpp = ClientXMPP(...)
+ xmpp.use_proxy = True
+ xmpp.proxy_config = {
+ 'host': 'proxy.example.com',
+ 'port': 5555,
+ 'username': 'example_user',
+ 'password': '******'
+ }
+
+The ``'username'`` and ``'password'`` fields are optional if the proxy does not
+require authentication.
+
+
+The Final Product
+-----------------
+
+.. include:: ../../examples/proxy_echo_client.py
+ :literal:
diff --git a/docs/getting_started/scheduler.rst b/docs/getting_started/scheduler.rst
new file mode 100644
index 00000000..a9263a11
--- /dev/null
+++ b/docs/getting_started/scheduler.rst
@@ -0,0 +1,2 @@
+Send a Message Every 5 Minutes
+==============================
diff --git a/docs/getting_started/sendlogout.rst b/docs/getting_started/sendlogout.rst
new file mode 100644
index 00000000..a1352db9
--- /dev/null
+++ b/docs/getting_started/sendlogout.rst
@@ -0,0 +1,94 @@
+Sign in, Send a Message, and Disconnect
+=======================================
+
+.. note::
+
+ If you have any issues working through this quickstart guide
+ or the other tutorials here, please either send a message to the
+ `mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
+ or join the chat room at `sleek@conference.jabber.org
+ <xmpp:sleek@conference.jabber.org?join>`_.
+
+A common use case for SleekXMPP is to send one-off messages from
+time to time. For example, one use case could be sending out a notice when
+a shell script finishes a task.
+
+We will create our one-shot bot based on the pattern explained in :ref:`echobot`. To
+start, we create a client class based on :class:`ClientXMPP <sleekxmpp.clientxmpp.ClientXMPP>` and
+register a handler for the :term:`session_start` event. We will also accept parameters
+for the JID that will receive our message, and the string content of the message.
+
+.. code-block:: python
+
+ import sleekxmpp
+
+
+ class SendMsgBot(sleekxmpp.ClientXMPP):
+
+ def __init__(self, jid, password, recipient, msg):
+ super(SendMsgBot, self).__init__(jid, password)
+
+ self.recipient = recipient
+ self.msg = msg
+
+ self.add_event_handler('session_start', self.start)
+
+ def start(self, event):
+ self.send_presence()
+ self.get_roster()
+
+Note that as in :ref:`echobot`, we need to include send an initial presence and request
+the roster. Next, we want to send our message, and to do that we will use :meth:`send_message <sleekxmpp.basexmpp.BaseXMPP.send_message>`.
+
+.. code-block:: python
+
+ def start(self, event):
+ self.send_presence()
+ self.get_roster()
+
+ self.send_message(mto=self.recipient, mbody=self.msg)
+
+Finally, we need to disconnect the client using :meth:`disconnect <sleekxmpp.xmlstream.XMLStream.disconnect>`.
+Now, sent stanzas are placed in a queue to pass them to the send thread. If we were to call
+:meth:`disconnect <sleekxmpp.xmlstream.XMLStream.disconnect>` without any parameters, then it is possible
+for the client to disconnect before the send queue is processed and the message is actually
+sent on the wire. To ensure that our message is processed, we use
+:meth:`disconnect(wait=True) <sleekxmpp.xmlstream.XMLStream.disconnect>`.
+
+.. code-block:: python
+
+ def start(self, event):
+ self.send_presence()
+ self.get_roster()
+
+ self.send_message(mto=self.recipient, mbody=self.msg)
+
+ self.disconnect(wait=True)
+
+.. warning::
+
+ If you happen to be adding stanzas to the send queue faster than the send thread
+ can process them, then :meth:`disconnect(wait=True) <sleekxmpp.xmlstream.XMLStream.disconnect>`
+ will block and not disconnect.
+
+Final Product
+-------------
+
+.. compound::
+
+ The final step is to create a small runner script for initialising our ``SendMsgBot`` class and adding some
+ basic configuration options. By following the basic boilerplate pattern in :ref:`echobot`, we arrive
+ at the code below. To experiment with this example, you can use:
+
+ .. code-block:: sh
+
+ python send_client.py -d -j oneshot@example.com -t someone@example.net -m "This is a message"
+
+ which will prompt for the password and then log in, send your message, and then disconnect. To test, open
+ your regular IM client with the account you wish to send messages to. When you run the ``send_client.py``
+ example and instruct it to send your IM client account a message, you should receive the message you
+ gave. If the two JIDs you use also have a mutual presence subscription (they're on each other's buddy lists)
+ then you will also see the ``SendMsgBot`` client come online and then go offline.
+
+.. include:: ../../examples/send_client.py
+ :literal:
diff --git a/docs/glossary.rst b/docs/glossary.rst
new file mode 100644
index 00000000..35d2dc86
--- /dev/null
+++ b/docs/glossary.rst
@@ -0,0 +1,35 @@
+.. _glossary:
+
+Glossary
+========
+
+.. glossary::
+ :sorted:
+
+ stream handler
+ A callback function that accepts stanza objects pulled directly
+ from the XML stream. A stream handler is encapsulated in a
+ object that includes a :term:`Matcher` object, and which provides
+ additional semantics. For example, the ``Waiter`` handler wrapper
+ blocks thread execution until a matching stanza is received.
+
+ event handler
+ A callback function that responds to events raised by
+ ``XMLStream.event``. An event handler may be marked as
+ threaded, allowing it to execute outside of the main processing
+ loop.
+
+ stanza object
+ Informally may refer both to classes which extend ``ElementBase``
+ or ``StanzaBase``, and to objects of such classes.
+
+ A stanza object is a wrapper for an XML object which exposes ``dict``
+ like interfaces which may be assigned to, read from, or deleted.
+
+ stanza plugin
+ A :term:`stanza object` which has been registered as a potential child
+ of another stanza object. The plugin stanza may accessed through the
+ parent stanza using the plugin's ``plugin_attrib`` as an interface.
+
+ substanza
+ See :term:`stanza plugin`
diff --git a/docs/guide_xep_0030.rst b/docs/guide_xep_0030.rst
new file mode 100644
index 00000000..cb8d7d25
--- /dev/null
+++ b/docs/guide_xep_0030.rst
@@ -0,0 +1,201 @@
+XEP-0030: Working with Service Discovery
+========================================
+
+XMPP networks can be composed of many individual clients, components,
+and servers. Determining the JIDs for these entities and the various
+features they may support is the role of `XEP-0030, Service
+Discovery <http://xmpp.org/extensions/xep-0030.html>`_, or "disco" for short.
+
+Every XMPP entity may possess what are called nodes. A node is just a name for
+some aspect of an XMPP entity. For example, if an XMPP entity provides `Ad-Hoc
+Commands <http://xmpp.org/extensions/xep-0050.html>`_, then it will have a node
+named ``http://jabber.org/protocol/commands`` which will contain information
+about the commands provided. Other agents using these ad-hoc commands will
+interact with the information provided by this node. Note that the node name is
+just an identifier; there is no inherent meaning.
+
+Working with service discovery is about creating and querying these nodes.
+According to XEP-0030, a node may contain three types of information:
+identities, features, and items. (Further, extensible, information types are
+defined in `XEP-0128 <http://xmpp.org/extensions/xep-0128.html>`_, but they are
+not yet implemented by SleekXMPP.) SleekXMPP provides methods to configure each
+of these node attributes.
+
+Configuring Service Discovery
+-----------------------------
+The design focus for the XEP-0030 plug-in is handling info and items requests
+in a dynamic fashion, allowing for complex policy decisions of who may receive
+information and how much, or use alternate backend storage mechanisms for all
+of the disco data. To do this, each action that the XEP-0030 plug-in performs
+is handed off to what is called a "node handler," which is just a callback
+function. These handlers are arranged in a hierarchy that allows for a single
+handler to manage an entire domain of JIDs (say for a component), while allowing
+other handler functions to override that global behaviour for certain JIDs, or
+even further limited to only certain JID and node combinations.
+
+The Dynamic Handler Hierarchy
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* ``global``: (JID is None, node is None)
+
+ Handlers assigned at this level for an action (such as ``add_feature``) provide a global default
+ behaviour when the action is performed.
+
+* ``jid``: (JID assigned, node is None)
+
+ At this level, handlers provide a default behaviour for actions affecting any node owned by the
+ JID in question. This level is most useful for component connections; there is effectively no
+ difference between this and the global level when using a client connection.
+
+* ``node``: (JID assigned, node assigned)
+
+ A handler for this level is responsible for carrying out an action for only one node, and is the
+ most specific handler type available. These types of handlers will be most useful for "special"
+ nodes that require special processing different than others provided by the JID, such as using
+ access control lists, or consolidating data from other nodes.
+
+Default Static Handlers
+~~~~~~~~~~~~~~~~~~~~~~~
+The XEP-0030 plug-in provides a default set of handlers that work using in-memory
+disco stanzas. Each handler simply performs the appropriate lookup or storage
+operation using these stanzas without doing any complex operations such as
+checking an ACL, etc.
+
+You may find it necessary at some point to revert a particular node or JID to
+using the default, static handlers. To do so, use the method ``make_static()``.
+You may also elect to only convert a given set of actions instead.
+
+Creating a Node Handler
+~~~~~~~~~~~~~~~~~~~~~~~
+Every node handler receives three arguments: the JID, the node, and a data
+parameter that will contain the relevant information for carrying out the
+handler's action, typically a dictionary.
+
+The JID will always have a value, defaulting to ``xmpp.boundjid.full`` for
+components or ``xmpp.boundjid.bare`` for clients. The node value may be None or
+a string.
+
+Only handlers for the actions ``get_info`` and ``get_items`` need to have return
+values. For these actions, DiscoInfo or DiscoItems stanzas are exepected as
+output. It is also acceptable for handlers for these actions to generate an
+XMPPError exception when necessary.
+
+Example Node Handler:
++++++++++++++++++++++
+Here is one of the built-in default handlers as an example:
+
+.. code-block:: python
+
+ def add_identity(self, jid, node, data):
+ """
+ Add a new identity to the JID/node combination.
+
+ The data parameter may provide:
+ category -- The general category to which the agent belongs.
+ itype -- A more specific designation with the category.
+ name -- Optional human readable name for this identity.
+ lang -- Optional standard xml:lang value.
+ """
+ self.add_node(jid, node)
+ self.nodes[(jid, node)]['info'].add_identity(
+ data.get('category', ''),
+ data.get('itype', ''),
+ data.get('name', None),
+ data.get('lang', None))
+
+Adding Identities, Features, and Items
+--------------------------------------
+In order to maintain some backwards compatibility, the methods ``add_identity``,
+``add_feature``, and ``add_item`` do not follow the method signature pattern of
+the other API methods (i.e. jid, node, then other options), but rather retain
+the parameter orders from previous plug-in versions.
+
+Adding an Identity
+~~~~~~~~~~~~~~~~~~
+Adding an identity may be done using either the older positional notation, or
+with keyword parameters. The example below uses the keyword arguments, but in
+the same order as expected using positional arguments.
+
+.. code-block:: python
+
+ xmpp['xep_0030'].add_identity(category='client',
+ itype='bot',
+ name='Sleek',
+ node='foo',
+ jid=xmpp.boundjid.full,
+ lang='no')
+
+The JID and node values determine which handler will be used to perform the
+``add_identity`` action.
+
+The ``lang`` parameter allows for adding localized versions of identities using
+the ``xml:lang`` attribute.
+
+Adding a Feature
+~~~~~~~~~~~~~~~~
+The position ordering for ``add_feature()`` is to include the feature, then
+specify the node and then the JID. The JID and node values determine which
+handler will be used to perform the ``add_feature`` action.
+
+.. code-block:: python
+
+ xmpp['xep_0030'].add_feature(feature='jabber:x:data',
+ node='foo',
+ jid=xmpp.boundjid.full)
+
+Adding an Item
+~~~~~~~~~~~~~~
+The parameters to ``add_item()`` are potentially confusing due to the fact that
+adding an item requires two JID and node combinations: the JID and node of the
+item itself, and the JID and node that will own the item.
+
+.. code-block:: python
+
+ xmpp['xep_0030'].add_item(jid='myitemjid@example.com',
+ name='An Item!',
+ node='owner_node',
+ subnode='item_node',
+ ijid=xmpp.boundjid.full)
+
+.. note::
+
+ In this case, the owning JID and node are provided with the
+ parameters ``ijid`` and ``node``.
+
+Peforming Disco Queries
+-----------------------
+The methods ``get_info()`` and ``get_items()`` are used to query remote JIDs
+and their nodes for disco information. Since these methods are wrappers for
+sending Iq stanzas, they also accept all of the parameters of the ``Iq.send()``
+method. The ``get_items()`` method may also accept the boolean parameter
+``iterator``, which when set to ``True`` will return an iterator object using
+the `XEP-0059 <http://xmpp.org/extensions/xep-0059.html>`_ plug-in.
+
+.. code-block:: python
+
+ info = self['xep_0030'].get_info(jid='foo@example.com',
+ node='bar',
+ ifrom='baz@mycomponent.example.com',
+ block=True,
+ timeout=30)
+
+ items = self['xep_0030'].get_info(jid='foo@example.com',
+ node='bar',
+ iterator=True)
+
+For more examples on how to use basic disco queries, check the ``disco_browser.py``
+example in the ``examples`` directory.
+
+Local Queries
+~~~~~~~~~~~~~
+In some cases, it may be necessary to query the contents of a node owned by the
+client itself, or one of a component's many JIDs. The same method is used as for
+normal queries, with two differences. First, the parameter ``local=True`` must
+be used. Second, the return value will be a DiscoInfo or DiscoItems stanza, not
+a full Iq stanza.
+
+.. code-block:: python
+
+ info = self['xep_0030'].get_info(node='foo', local=True)
+ items = self['xep_0030'].get_items(jid='somejid@mycomponent.example.com',
+ node='bar',
+ local=True)
diff --git a/docs/handlersmatchers.rst b/docs/handlersmatchers.rst
new file mode 100644
index 00000000..628c4142
--- /dev/null
+++ b/docs/handlersmatchers.rst
@@ -0,0 +1,4 @@
+.. _using-handlers-matchers:
+
+Using Stream Handlers and Matchers
+==================================
diff --git a/docs/howto/stanzas.rst b/docs/howto/stanzas.rst
new file mode 100644
index 00000000..d52a90d4
--- /dev/null
+++ b/docs/howto/stanzas.rst
@@ -0,0 +1,30 @@
+.. _work-with-stanzas:
+
+How to Work with Stanza Objects
+===============================
+
+
+.. _create-stanza-interfaces:
+
+Defining Stanza Interfaces
+--------------------------
+
+
+.. _create-stanza-plugins:
+
+Creating Stanza Plugins
+-----------------------
+
+
+
+.. _create-extension-plugins:
+
+Creating a Stanza Extension
+---------------------------
+
+
+
+.. _override-parent-interfaces:
+
+Overriding a Parent Stanza
+--------------------------
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 00000000..fc6541d6
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,179 @@
+SleekXMPP
+#########
+
+.. sidebar:: Get the Code
+
+ .. code-block:: sh
+
+ pip install sleekxmpp
+
+ The latest source code for SleekXMPP may be found on `Github
+ <http://github.com/fritzy/SleekXMPP>`_. Releases can be found in the
+ ``master`` branch, while the latest development version is in the
+ ``develop`` branch.
+
+ **Latest Stable Release**
+ - `1.0 RC3 <http://github.com/fritzy/SleekXMPP/zipball/1.0-RC3>`_
+
+ **Develop Releases**
+ - `Latest Develop Version <http://github.com/fritzy/SleekXMPP/zipball/develop>`_
+
+
+ A mailing list and XMPP chat room are available for discussing and getting
+ help with SleekXMPP.
+
+ **Mailing List**
+ `SleekXMPP Discussion on Google Groups <http://groups.google.com/group/sleekxmpp-discussion>`_
+
+ **Chat**
+ `sleek@conference.jabber.org <xmpp:sleek@conference.jabber.org?join>`_
+
+
+SleekXMPP is an :ref:`MIT licensed <license>` XMPP library for Python 2.6/3.1+,
+and is featured in examples in
+`XMPP: The Definitive Guide <http://oreilly.com/catalog/9780596521271>`_
+by Kevin Smith, Remko Tronçon, and Peter Saint-Andre. If you've arrived
+here from reading the Definitive Guide, please see the notes on updating
+the examples to the latest version of SleekXMPP.
+
+SleekXMPP's design goals and philosphy are:
+
+**Low number of dependencies**
+ Installing and using SleekXMPP should be as simple as possible, without
+ having to deal with long dependency chains.
+
+ As part of reducing the number of dependencies, some third party
+ modules are included with SleekXMPP in the ``thirdparty`` directory.
+ Imports from this module first try to import an existing installed
+ version before loading the packaged version, when possible.
+
+**Every XEP as a plugin**
+ Following Python's "batteries included" approach, the goal is to
+ provide support for all currently active XEPs (final and draft). Since
+ adding XEP support is done through easy to create plugins, the hope is
+ to also provide a solid base for implementing and creating experimental
+ XEPs.
+
+**Rewarding to work with**
+ As much as possible, SleekXMPP should allow things to "just work" using
+ sensible defaults and appropriate abstractions. XML can be ugly to work
+ with, but it doesn't have to be that way.
+
+Getting Started (with Examples)
+-------------------------------
+.. toctree::
+ :maxdepth: 1
+
+ getting_started/echobot
+ getting_started/sendlogout
+ getting_started/component
+ getting_started/presence
+ getting_started/muc
+ getting_started/proxy
+ getting_started/scheduler
+ getting_started/iq
+
+
+Tutorials, FAQs, and How To Guides
+----------------------------------
+.. toctree::
+ :maxdepth: 1
+
+ faq
+ xeps
+ xmpp_tdg
+ howto/stanzas
+ create_plugin
+ features
+ sasl
+ handlersmatchers
+
+Plugin Guides
+~~~~~~~~~~~~~
+.. toctree::
+ :maxdepth: 1
+
+ guide_xep_0030
+
+SleekXMPP Architecture and Design
+---------------------------------
+.. toctree::
+ :maxdepth: 3
+
+ architecture
+ plugin_arch
+
+API Reference
+-------------
+.. toctree::
+ :maxdepth: 2
+
+ event_index
+ api/clientxmpp
+ api/componentxmpp
+ api/basexmpp
+ api/exceptions
+ api/xmlstream/jid
+ api/xmlstream/stanzabase
+ api/xmlstream/handler
+ api/xmlstream/matcher
+ api/xmlstream/xmlstream
+ api/xmlstream/scheduler
+ api/xmlstream/tostring
+ api/xmlstream/filesocket
+
+Core Stanzas
+~~~~~~~~~~~~
+.. toctree::
+ :maxdepth: 2
+
+ api/stanza/rootstanza
+ api/stanza/message
+ api/stanza/presence
+ api/stanza/iq
+ api/stanza/error
+ api/stanza/stream_error
+
+Plugins
+~~~~~~~
+.. toctree::
+ :maxdepth: 2
+
+
+Additional Info
+---------------
+.. toctree::
+ :hidden:
+
+ glossary
+ license
+
+* :ref:`license`
+* :ref:`glossary`
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
+Credits
+-------
+**Main Author:** Nathan Fritz
+ `fritzy@netflint.net <xmpp:fritzy@netflint.net?message>`_,
+ `@fritzy <http://twitter.com/fritzy>`_
+
+ Nathan is also the author of XMPPHP and `Seesmic-AS3-XMPP
+ <http://code.google.com/p/seesmic-as3-xmpp/>`_, and a member of the XMPP
+ Council.
+
+**Co-Author:** Lance Stout
+ `lancestout@gmail.com <xmpp:lancestout@gmail.com?message>`_,
+ `@lancestout <http://twitter.com/lancestout>`_
+
+**Contributors:**
+ - Brian Beggs (`macdiesel <http://github.com/macdiesel>`_)
+ - Dann Martens (`dannmartens <http://github.com/dannmartens>`_)
+ - Florent Le Coz (`louiz <http://github.com/louiz>`_)
+ - Kevin Smith (`Kev <http://github.com/Kev>`_, http://kismith.co.uk)
+ - Remko Tronçon (`remko <http://github.com/remko>`_, http://el-tramo.be)
+ - Te-jé Rogers (`te-je <http://github.com/te-je>`_)
+ - Thom Nichols (`tomstrummer <http://github.com/tomstrummer>`_)
+
diff --git a/docs/license.rst b/docs/license.rst
new file mode 100644
index 00000000..cbcf5c11
--- /dev/null
+++ b/docs/license.rst
@@ -0,0 +1,5 @@
+.. _license:
+
+License (MIT)
+=============
+.. include:: ../LICENSE
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 00000000..d97407a6
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,170 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+)
+
+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. changes to make an overview over all changed/added/deprecated items
+ 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
+)
+
+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\SleekXMPP.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\SleekXMPP.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" == "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" == "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
+)
+
+:end
diff --git a/docs/plugin_arch.rst b/docs/plugin_arch.rst
new file mode 100644
index 00000000..0141b793
--- /dev/null
+++ b/docs/plugin_arch.rst
@@ -0,0 +1,2 @@
+Plugin Architecture
+===================
diff --git a/docs/python-objects.inv b/docs/python-objects.inv
new file mode 100644
index 00000000..b7afc07e
--- /dev/null
+++ b/docs/python-objects.inv
Binary files differ
diff --git a/docs/sasl.rst b/docs/sasl.rst
new file mode 100644
index 00000000..46c45c2a
--- /dev/null
+++ b/docs/sasl.rst
@@ -0,0 +1,2 @@
+How SASL Authentication Works
+=============================
diff --git a/docs/xeps.rst b/docs/xeps.rst
new file mode 100644
index 00000000..3653d10a
--- /dev/null
+++ b/docs/xeps.rst
@@ -0,0 +1,50 @@
+Supported XEPS
+==============
+
+======= ============================= ================
+XEP Description Notes
+======= ============================= ================
+`0004`_ Data forms
+`0009`_ Jabber RPC
+`0012`_ Last Activity
+`0030`_ Service Discovery
+`0033`_ Extended Stanza Addressing
+`0045`_ Multi-User Chat (MUC) Client-side only
+`0050`_ Ad-hoc Commands
+`0059`_ Result Set Management
+`0060`_ Publish/Subscribe (PubSub) Client-side only
+`0066`_ Out-of-band Data
+`0078`_ Non-SASL Authentication
+`0082`_ XMPP Date and Time Profiles
+`0085`_ Chat-State Notifications
+`0086`_ Error Condition Mappings
+`0092`_ Software Version
+`0128`_ Service Discovery Extensions
+`0202`_ Entity Time
+`0203`_ Delayed Delivery
+`0224`_ Attention
+`0249`_ Direct MUC Invitations
+======= ============================= ================
+
+
+.. _0004: http://xmpp.org/extensions/xep-0004.html
+.. _0009: http://xmpp.org/extensions/xep-0009.html
+.. _0012: http://xmpp.org/extensions/xep-0012.html
+.. _0030: http://xmpp.org/extensions/xep-0030.html
+.. _0033: http://xmpp.org/extensions/xep-0033.html
+.. _0045: http://xmpp.org/extensions/xep-0045.html
+.. _0050: http://xmpp.org/extensions/xep-0050.html
+.. _0059: http://xmpp.org/extensions/xep-0059.html
+.. _0060: http://xmpp.org/extensions/xep-0060.html
+.. _0066: http://xmpp.org/extensions/xep-0066.html
+.. _0078: http://xmpp.org/extensions/xep-0078.html
+.. _0082: http://xmpp.org/extensions/xep-0082.html
+.. _0085: http://xmpp.org/extensions/xep-0085.html
+.. _0086: http://xmpp.org/extensions/xep-0086.html
+.. _0092: http://xmpp.org/extensions/xep-0092.html
+.. _0128: http://xmpp.org/extensions/xep-0128.html
+.. _0199: http://xmpp.org/extensions/xep-0199.html
+.. _0202: http://xmpp.org/extensions/xep-0202.html
+.. _0203: http://xmpp.org/extensions/xep-0203.html
+.. _0224: http://xmpp.org/extensions/xep-0224.html
+.. _0249: http://xmpp.org/extensions/xep-0249.html
diff --git a/docs/xmpp_tdg.rst b/docs/xmpp_tdg.rst
new file mode 100644
index 00000000..3d12b1b6
--- /dev/null
+++ b/docs/xmpp_tdg.rst
@@ -0,0 +1,249 @@
+Following *XMPP: The Definitive Guide*
+======================================
+
+SleekXMPP was featured in the first edition of the O'Reilly book
+`XMPP: The Definitive Guide <http://oreilly.com/catalog/9780596521271/>`_
+by Peter Saint-Andre, Kevin Smith, and Remko Tronçon. The original source code
+for the book's examples can be found at http://github.com/remko/xmpp-tdg. An
+updated version of the source code, maintained to stay current with the latest
+SleekXMPP release, is available at http://github.com/legastero/xmpp-tdg.
+
+However, since publication, SleekXMPP has advanced from version 0.2.1 to version
+1.0 and there have been several major API changes. The most notable is the
+introduction of :term:`stanza objects <stanza object>` which have simplified and
+standardized interactions with the XMPP XML stream.
+
+What follows is a walk-through of *The Definitive Guide* highlighting the
+changes needed to make the code examples work with version 1.0 of SleekXMPP.
+These changes have been kept to a minimum to preserve the correlation with
+the book's explanations, so be aware that some code may not use current best
+practices.
+
+Example 2-2. (Page 26)
+----------------------
+
+**Implementation of a basic bot that echoes all incoming messages back to its sender.**
+
+The echo bot example requires a change to the ``handleIncomingMessage`` method
+to reflect the use of the ``Message`` :term:`stanza object`. The
+``"jid"`` field of the message object should now be ``"from"`` to match the
+``from`` attribute of the actual XML message stanza. Likewise, ``"message"``
+changes to ``"body"`` to match the ``body`` element of the message stanza.
+
+Updated Code
+~~~~~~~~~~~~
+
+.. code-block:: python
+
+ def handleIncomingMessage(self, message):
+ self.xmpp.sendMessage(message["from"], message["body"])
+
+`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/EchoBot/EchoBot.py>`_ |
+`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/EchoBot/EchoBot.py>`_
+
+Example 14-1. (Page 215)
+------------------------
+
+**CheshiR IM bot implementation.**
+
+The main event handling method in the Bot class is meant to process both message
+events and presence update events. With the new changes in SleekXMPP 1.0,
+extracting a CheshiR status "message" from both types of stanzas
+requires accessing different attributes. In the case of a message stanza, the
+``"body"`` attribute would contain the CheshiR message. For a presence event,
+the information is stored in the ``"status"`` attribute. To handle both cases,
+we can test the type of the given event object and look up the proper attribute
+based on the type.
+
+Like in the EchoBot example, the expression ``event["jid"]`` needs to change
+to ``event["from"]`` in order to get a JID object for the stanza's sender.
+Because other functions in CheshiR assume that the JID is a string, the ``jid``
+attribute is used to access the string version of the JID. A check is also added
+in case ``user`` is ``None``, but the check could (and probably should) be
+placed in ``addMessageFromUser``.
+
+Another change is needed in ``handleMessageAddedToBackend`` where
+an HTML-IM response is created. The HTML content should be enclosed in a single
+element, such as a ``<p>`` tag.
+
+Updated Code
+~~~~~~~~~~~~
+
+.. code-block:: python
+
+ def handleIncomingXMPPEvent(self, event):
+ msgLocations = {sleekxmpp.stanza.presence.Presence: "status",
+ sleekxmpp.stanza.message.Message: "body"}
+
+ message = event[msgLocations[type(event)]]
+ user = self.backend.getUserFromJID(event["from"].jid)
+ if user is not None:
+ self.backend.addMessageFromUser(message, user)
+
+ def handleMessageAddedToBackend(self, message) :
+ body = message.user + ": " + message.text
+ htmlBody = "<p><a href='%(uri)s'>%(user)s</a>: %(message)s</p>" % {
+ "uri": self.url + "/" + message.user,
+ "user" : message.user, "message" : message.text }
+ for subscriberJID in self.backend.getSubscriberJIDs(message.user) :
+ self.xmpp.sendMessage(subscriberJID, body, mhtml=htmlBody)
+
+`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/Bot.py>`_ |
+`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/Bot.py>`_
+
+
+Example 14-3. (Page 217)
+------------------------
+**Configurable CheshiR IM bot implementation.**
+
+.. note::
+ Since the CheshiR examples build on each other, see previous sections for
+ corrections to code that is not marked as new in the book example.
+
+The main difference for the configurable IM bot is the handling for the
+data form in ``handleConfigurationCommand``. The test for equality
+with the string ``"1"`` is no longer required; SleekXMPP converts
+boolean data form fields to the values ``True`` and ``False``
+automatically.
+
+For the method ``handleIncomingXMPPPresence``, the attribute
+``"jid"`` is again converted to ``"from"`` to get a JID
+object for the presence stanza's sender, and the ``jid`` attribute is
+used to access the string version of that JID object. A check is also added in
+case ``user`` is ``None``, but the check could (and probably
+should) be placed in ``getShouldMonitorPresenceFromUser``.
+
+Updated Code
+~~~~~~~~~~~~
+
+.. code-block:: python
+
+ def handleConfigurationCommand(self, form, sessionId):
+ values = form.getValues()
+ monitorPresence =values["monitorPresence"]
+ jid = self.xmpp.plugin["xep_0050"].sessions[sessionId]["jid"]
+ user = self.backend.getUserFromJID(jid)
+ self.backend.setShouldMonitorPresenceFromUser(user, monitorPresence)
+
+ def handleIncomingXMPPPresence(self, event):
+ user = self.backend.getUserFromJID(event["from"].jid)
+ if user is not None:
+ if self.backend.getShouldMonitorPresenceFromUser(user):
+ self.handleIncomingXMPPEvent(event)
+
+`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/ConfigurableBot.py>`_ |
+`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/ConfigurableBot.py>`_
+
+
+Example 14-4. (Page 220)
+------------------------
+**CheshiR IM server component implementation.**
+
+.. note::
+ Since the CheshiR examples build on each other, see previous sections for
+ corrections to code that is not marked as new in the book example.
+
+Like several previous examples, a needed change is to replace
+``subscription["from"]`` with ``subscription["from"].jid`` because the
+``BaseXMPP`` method ``makePresence`` requires the JID to be a string.
+
+A correction needs to be made in ``handleXMPPPresenceProbe`` because a line was
+left out of the original implementation; the variable ``user`` is undefined. The
+JID of the user can be extracted from the presence stanza's ``from`` attribute.
+
+Since this implementation of CheshiR uses an XMPP component, it must
+include a ``from`` attribute in all messages that it sends. Adding the
+``from`` attribute is done by including ``mfrom=self.xmpp.jid`` in calls to
+``self.xmpp.sendMessage``.
+
+Updated Code
+~~~~~~~~~~~~
+
+.. code-block:: python
+
+ def handleXMPPPresenceProbe(self, event) :
+ self.xmpp.sendPresence(pto = event["from"])
+
+ def handleXMPPPresenceSubscription(self, subscription) :
+ if subscription["type"] == "subscribe" :
+ userJID = subscription["from"].jid
+ self.xmpp.sendPresenceSubscription(pto=userJID, ptype="subscribed")
+ self.xmpp.sendPresence(pto = userJID)
+ self.xmpp.sendPresenceSubscription(pto=userJID, ptype="subscribe")
+
+ def handleMessageAddedToBackend(self, message) :
+ body = message.user + ": " + message.text
+ for subscriberJID in self.backend.getSubscriberJIDs(message.user) :
+ self.xmpp.sendMessage(subscriberJID, body, mfrom=self.xmpp.jid)
+
+`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/SimpleComponent.py>`_ |
+`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/SimpleComponent.py>`_
+
+
+Example 14-6. (Page 223)
+------------------------
+**CheshiR IM server component with in-band registration support.**
+
+.. note::
+ Since the CheshiR examples build on each other, see previous sections for
+ corrections to code that is not marked as new in the book example.
+
+After applying the changes from Example 14-4 above, the registrable component
+implementation should work correctly.
+
+.. tip::
+ To see how to implement in-band registration as a SleekXMPP plugin,
+ see the tutorial :ref:`tutorial-create-plugin`.
+
+`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/RegistrableComponent.py>`_ |
+`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/RegistrableComponent.py>`_
+
+Example 14-7. (Page 225)
+------------------------
+**Extended CheshiR IM server component implementation.**
+
+.. note::
+ Since the CheshiR examples build on each other, see previous
+ sections for corrections to code that is not marked as new in the book
+ example.
+
+While the final code example can look daunting with all of the changes
+made, it requires very few modifications to work with the latest version of
+SleekXMPP. Most differences are the result of CheshiR's backend functions
+expecting JIDs to be strings so that they can be stripped to bare JIDs. To
+resolve these, use the ``jid`` attribute of the JID objects. Also,
+references to ``"message"`` and ``"jid"`` attributes need to
+be changed to either ``"body"`` or ``"status"``, and either
+``"from"`` or ``"to"`` depending on if the object is a message
+or presence stanza and which of the JIDs from the stanza is needed.
+
+Updated Code
+~~~~~~~~~~~~
+
+.. code-block:: python
+
+ def handleIncomingXMPPMessage(self, event) :
+ message = self.addRecipientToMessage(event["body"], event["to"].jid)
+ user = self.backend.getUserFromJID(event["from"].jid)
+ self.backend.addMessageFromUser(message, user)
+
+ def handleIncomingXMPPPresence(self, event) :
+ if event["to"].jid == self.componentDomain :
+ user = self.backend.getUserFromJID(event["from"].jid)
+ self.backend.addMessageFromUser(event["status"], user)
+
+ ...
+
+ def handleXMPPPresenceSubscription(self, subscription) :
+ if subscription["type"] == "subscribe" :
+ userJID = subscription["from"].jid
+ user = self.backend.getUserFromJID(userJID)
+ contactJID = subscription["to"]
+ self.xmpp.sendPresenceSubscription(
+ pfrom=contactJID, pto=userJID, ptype="subscribed", pnick=user)
+ self.sendPresenceOfContactToUser(contactJID=contactJID, userJID=userJID)
+ if contactJID == self.componentDomain :
+ self.sendAllContactSubscriptionRequestsToUser(userJID)
+
+`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/Component.py>`_ |
+`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/Component.py>`_