Compare commits
	
		
			135 Commits
		
	
	
		
			sleek-merg
			...
			sleek-0.9-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					494e3ef449 | ||
| 
						 | 
					be5688007b | ||
| 
						 | 
					ad7c1b06f4 | ||
| 
						 | 
					083ac3faaf | ||
| 
						 | 
					a909731b03 | ||
| 
						 | 
					4864197d46 | ||
| 
						 | 
					92a5ac2ba9 | ||
| 
						 | 
					02ca5f0e42 | ||
| 
						 | 
					1e009513ee | ||
| 
						 | 
					55f83e8ab0 | ||
| 
						 | 
					d43fba3c8f | ||
| 
						 | 
					9c5285987d | ||
| 
						 | 
					d09cbef9a7 | ||
| 
						 | 
					9c850f080d | ||
| 
						 | 
					879dd11daa | ||
| 
						 | 
					969c4652a4 | ||
| 
						 | 
					9506970042 | ||
| 
						 | 
					3c6b07353d | ||
| 
						 | 
					66c6c21ad8 | ||
| 
						 | 
					c5b5cc4af1 | ||
| 
						 | 
					e835843aab | ||
| 
						 | 
					d6681f16d2 | ||
| 
						 | 
					fc952efae9 | ||
| 
						 | 
					f7273affc5 | ||
| 
						 | 
					34eb88f199 | ||
| 
						 | 
					f3cf5f6080 | ||
| 
						 | 
					85d8b9270f | ||
| 
						 | 
					259dffeb6e | ||
| 
						 | 
					0a30e6c017 | ||
| 
						 | 
					d381ab320a | ||
| 
						 | 
					6e93982fdf | ||
| 
						 | 
					33602f232c | ||
| 
						 | 
					7968ca2892 | ||
| 
						 | 
					661cdd2018 | ||
| 
						 | 
					4b00baab1e | ||
| 
						 | 
					fe1d3004cc | ||
| 
						 | 
					62da57a6c2 | ||
| 
						 | 
					ba9633f8f7 | ||
| 
						 | 
					065a164223 | ||
| 
						 | 
					cd2017b8b0 | ||
| 
						 | 
					dd9f33b7d9 | ||
| 
						 | 
					0a23f84ec3 | ||
| 
						 | 
					f477ccf533 | ||
| 
						 | 
					d62a30b0f8 | ||
| 
						 | 
					d763795b2c | ||
| 
						 | 
					fff54eaf2f | ||
| 
						 | 
					488d5b29d4 | ||
| 
						 | 
					9bdb297fe2 | ||
| 
						 | 
					fa7f72d0af | ||
| 
						 | 
					c538ffae79 | ||
| 
						 | 
					5d87a54913 | ||
| 
						 | 
					8bdfa77024 | ||
| 
						 | 
					15ac3e9fba | ||
| 
						 | 
					e8d37b409c | ||
| 
						 | 
					898f96f265 | ||
| 
						 | 
					bbf1cb8ba2 | ||
| 
						 | 
					d22f6a2aa5 | ||
| 
						 | 
					c0a6291fea | ||
| 
						 | 
					f5d0466462 | ||
| 
						 | 
					f659e3081e | ||
| 
						 | 
					4fccd77685 | ||
| 
						 | 
					bf2bf29fc6 | ||
| 
						 | 
					34dc236126 | ||
| 
						 | 
					9464736551 | ||
| 
						 | 
					47f1fb1690 | ||
| 
						 | 
					66cf0c2021 | ||
| 
						 | 
					e7c37c4ec5 | ||
| 
						 | 
					1aa34cb0fc | ||
| 
						 | 
					919c8c5633 | ||
| 
						 | 
					f54501a346 | ||
| 
						 | 
					d20cd6b3e6 | ||
| 
						 | 
					3f96226e29 | ||
| 
						 | 
					71d72f431f | ||
| 
						 | 
					da6e1e47dc | ||
| 
						 | 
					2f0f18a8c6 | ||
| 
						 | 
					1c32668e18 | ||
| 
						 | 
					77bff9cce7 | ||
| 
						 | 
					1f3cfb98f1 | ||
| 
						 | 
					4295a66c70 | ||
| 
						 | 
					8227affd7f | ||
| 
						 | 
					3a2f989c5e | ||
| 
						 | 
					85a2715c7d | ||
| 
						 | 
					b03e6168a8 | ||
| 
						 | 
					2a43f59a58 | ||
| 
						 | 
					184f7cb8a4 | ||
| 
						 | 
					e1aa4d0b93 | ||
| 
						 | 
					db4989c66d | ||
| 
						 | 
					7930ed22f2 | ||
| 
						 | 
					b0066f3ef4 | ||
| 
						 | 
					c0457cf5d0 | ||
| 
						 | 
					59b8406573 | ||
| 
						 | 
					686943a2ec | ||
| 
						 | 
					060b4c3938 | ||
| 
						 | 
					49f5767aea | ||
| 
						 | 
					4eb210bff5 | ||
| 
						 | 
					1780ca900a | ||
| 
						 | 
					e6c2fde283 | ||
| 
						 | 
					ecf902bf16 | ||
| 
						 | 
					d76c0931ef | ||
| 
						 | 
					e18793152f | ||
| 
						 | 
					e388680269 | ||
| 
						 | 
					bee42e4a2f | ||
| 
						 | 
					8e3227ae5e | ||
| 
						 | 
					257bcadd96 | ||
| 
						 | 
					3e5cdc8664 | ||
| 
						 | 
					194e6bcb51 | ||
| 
						 | 
					2e7024419a | ||
| 
						 | 
					5235313aab | ||
| 
						 | 
					a2719b0bb0 | ||
| 
						 | 
					71ad715caa | ||
| 
						 | 
					d452085049 | ||
| 
						 | 
					8b3b8aca9e | ||
| 
						 | 
					e00dea7c0c | ||
| 
						 | 
					520bf72e11 | ||
| 
						 | 
					040f426f1a | ||
| 
						 | 
					226b0e4297 | ||
| 
						 | 
					0b2cd176b1 | ||
| 
						 | 
					56b5cbe5b1 | ||
| 
						 | 
					3e83b16a58 | ||
| 
						 | 
					de4d611d30 | ||
| 
						 | 
					e8d0fc37dc | ||
| 
						 | 
					dda3e733b5 | ||
| 
						 | 
					4b322720b3 | ||
| 
						 | 
					3f41fdd231 | ||
| 
						 | 
					8e95ae2948 | ||
| 
						 | 
					341c110b6a | ||
| 
						 | 
					7522839141 | ||
| 
						 | 
					4c410dd48a | ||
| 
						 | 
					2d89954412 | ||
| 
						 | 
					a92075a659 | ||
| 
						 | 
					7552efee5c | ||
| 
						 | 
					6bc6ebb95d | ||
| 
						 | 
					e0c32b6d9b | ||
| 
						 | 
					1521a8b5c9 | ||
| 
						 | 
					70f69c180c | 
							
								
								
									
										17
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,15 +1,6 @@
 | 
				
			|||||||
*.py[co]
 | 
					*.pyc
 | 
				
			||||||
 | 
					.project
 | 
				
			||||||
build/
 | 
					build/
 | 
				
			||||||
dist/
 | 
					 | 
				
			||||||
MANIFEST
 | 
					 | 
				
			||||||
docs/_build/
 | 
					 | 
				
			||||||
*.swp
 | 
					*.swp
 | 
				
			||||||
.tox/
 | 
					.pydevproject
 | 
				
			||||||
.coverage
 | 
					.settings
 | 
				
			||||||
slixmpp.egg-info/
 | 
					 | 
				
			||||||
.ropeproject/
 | 
					 | 
				
			||||||
4913
 | 
					 | 
				
			||||||
*~
 | 
					 | 
				
			||||||
.baboon/
 | 
					 | 
				
			||||||
.DS_STORE
 | 
					 | 
				
			||||||
.idea/
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										69
									
								
								.pylintrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								.pylintrc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					# Pylint configuration file.
 | 
				
			||||||
 | 
					# run `pylint --generate-rcfile` to see the default configuration
 | 
				
			||||||
 | 
					# run `pylint --rcfile=.pylintrc smallfoot` to perform analysis
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Brain-dead errors regarding standard language features
 | 
				
			||||||
 | 
					#   W0142 = *args and **kwargs support
 | 
				
			||||||
 | 
					#   W0403 = Relative imports
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Pointless whining
 | 
				
			||||||
 | 
					#   R0201 = Method could be a function
 | 
				
			||||||
 | 
					#   W0212 = Accessing protected attribute of client class
 | 
				
			||||||
 | 
					#   W0613 = Unused argument
 | 
				
			||||||
 | 
					#   W0232 = Class has no __init__ method
 | 
				
			||||||
 | 
					#   R0903 = Too few public methods
 | 
				
			||||||
 | 
					#   C0301 = Line too long
 | 
				
			||||||
 | 
					#   R0913 = Too many arguments
 | 
				
			||||||
 | 
					#   C0103 = Invalid name
 | 
				
			||||||
 | 
					#   R0914 = Too many local variables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PyLint's module importation is unreliable
 | 
				
			||||||
 | 
					#   F0401 = Unable to import module
 | 
				
			||||||
 | 
					#   W0402 = Uses of a deprecated module
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Already an error when wildcard imports are used
 | 
				
			||||||
 | 
					#   W0614 = Unused import from wildcard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Sometimes disabled depending on how bad a module is
 | 
				
			||||||
 | 
					#   C0111 = Missing docstring
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Convention Errors related to whitespace:
 | 
				
			||||||
 | 
					#   C0321,C0322,C0323,C0324
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Comments that we've put in the code:
 | 
				
			||||||
 | 
					#   W0511
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[MESSAGES CONTROL]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Disable the message(s) with the given id(s).
 | 
				
			||||||
 | 
					disable=W0142,W0403,R0201,W0212,W0613,W0232,R0903,W0614,C0103,C0111,C0301,C0321,C0322,C0323,C0324,R0913,F0401,W0402,R0914,W0511,W0312
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[REPORTS]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					include-ids=y
 | 
				
			||||||
 | 
					reports=y
 | 
				
			||||||
 | 
					# Set the output format. Available formats are text, parseable, colorized, msvs
 | 
				
			||||||
 | 
					# (visual studio) and html
 | 
				
			||||||
 | 
					output-format=text
 | 
				
			||||||
 | 
					# Put messages in a separate file for each module / package specified on the
 | 
				
			||||||
 | 
					# command line instead of printing them on stdout. Reports (if any) will be
 | 
				
			||||||
 | 
					# written in a file name "pylint_global.[txt|html]".
 | 
				
			||||||
 | 
					files-output=no
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[VARIABLES]
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					# Tells whether we should check for unused import in __init__ files.
 | 
				
			||||||
 | 
					init-import=yes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[TYPECHECK]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# List of classes names for which member attributes should not be checked
 | 
				
			||||||
 | 
					# (useful for classes with attributes dynamically set).
 | 
				
			||||||
 | 
					#ignored-classes=Message
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[MISCELLANEOUS]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# List of note tags to take in consideration, separated by a comma.
 | 
				
			||||||
 | 
					notes=FIXME,XXX,TODO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										10
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -1,10 +0,0 @@
 | 
				
			|||||||
language: python
 | 
					 | 
				
			||||||
python:
 | 
					 | 
				
			||||||
  - "2.6"
 | 
					 | 
				
			||||||
  - "2.7"
 | 
					 | 
				
			||||||
  - "3.2"
 | 
					 | 
				
			||||||
  - "3.3"
 | 
					 | 
				
			||||||
  - "3.4"
 | 
					 | 
				
			||||||
install:
 | 
					 | 
				
			||||||
  - "pip install ."
 | 
					 | 
				
			||||||
script: testall.py
 | 
					 | 
				
			||||||
							
								
								
									
										11
									
								
								INSTALL
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								INSTALL
									
									
									
									
									
								
							@@ -1,13 +1,8 @@
 | 
				
			|||||||
Pre-requisites:
 | 
					Pre-requisites:
 | 
				
			||||||
- Python 3.4
 | 
					Python 3.1 or 2.6
 | 
				
			||||||
- Cython 0.22 and libidn, optionally (making JID faster by compiling the stringprep module)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Install:
 | 
					Install:
 | 
				
			||||||
> python3 setup.py install
 | 
					python3 setup.py install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Root install:
 | 
					Root install:
 | 
				
			||||||
> sudo python3 setup.py install
 | 
					sudo python3 setup.py install
 | 
				
			||||||
 | 
					 | 
				
			||||||
To test:
 | 
					 | 
				
			||||||
> cd examples
 | 
					 | 
				
			||||||
> python3 echo_client.py -d -j [USER@example.com] -p [PASSWORD]
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										177
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										177
									
								
								LICENSE
									
									
									
									
									
								
							@@ -1,4 +1,4 @@
 | 
				
			|||||||
Copyright (c) 2010 Nathanael C. Fritz
 | 
					Copyright (c) 2010 ICRL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
					Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
of this software and associated documentation files (the "Software"), to deal
 | 
					of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
@@ -17,178 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
					LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
					OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
				
			||||||
THE SOFTWARE.
 | 
					THE SOFTWARE.
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Licenses of Bundled Third Party Code
 | 
					 | 
				
			||||||
------------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
dateutil - Extensions to the standard python 2.3+ datetime module.
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Copyright (c) 2003-2011 - Gustavo Niemeyer <gustavo@niemeyer.net>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Redistribution and use in source and binary forms, with or without
 | 
					 | 
				
			||||||
modification, are permitted provided that the following conditions are met:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    * Redistributions of source code must retain the above copyright notice,
 | 
					 | 
				
			||||||
      this list of conditions and the following disclaimer.
 | 
					 | 
				
			||||||
    * Redistributions in binary form must reproduce the above copyright notice,
 | 
					 | 
				
			||||||
      this list of conditions and the following disclaimer in the documentation
 | 
					 | 
				
			||||||
      and/or other materials provided with the distribution.
 | 
					 | 
				
			||||||
    * Neither the name of the copyright holder nor the names of its
 | 
					 | 
				
			||||||
      contributors may be used to endorse or promote products derived from
 | 
					 | 
				
			||||||
      this software without specific prior written permission.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
					 | 
				
			||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
					 | 
				
			||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
					 | 
				
			||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 | 
					 | 
				
			||||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 | 
					 | 
				
			||||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | 
					 | 
				
			||||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
					 | 
				
			||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
					 | 
				
			||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
					 | 
				
			||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
					 | 
				
			||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fixed_datetime
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Copyright (c) 2008, Red Innovation Ltd., Finland
 | 
					 | 
				
			||||||
All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Redistribution and use in source and binary forms, with or without
 | 
					 | 
				
			||||||
modification, are permitted provided that the following conditions are met:
 | 
					 | 
				
			||||||
    * Redistributions of source code must retain the above copyright
 | 
					 | 
				
			||||||
      notice, this list of conditions and the following disclaimer.
 | 
					 | 
				
			||||||
    * Redistributions in binary form must reproduce the above copyright
 | 
					 | 
				
			||||||
      notice, this list of conditions and the following disclaimer in the
 | 
					 | 
				
			||||||
      documentation and/or other materials provided with the distribution.
 | 
					 | 
				
			||||||
    * Neither the name of Red Innovation nor the names of its contributors
 | 
					 | 
				
			||||||
      may be used to endorse or promote products derived from this software
 | 
					 | 
				
			||||||
      without specific prior written permission.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
THIS SOFTWARE IS PROVIDED BY RED INNOVATION ``AS IS'' AND ANY
 | 
					 | 
				
			||||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 | 
					 | 
				
			||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 | 
					 | 
				
			||||||
DISCLAIMED. IN NO EVENT SHALL RED INNOVATION BE LIABLE FOR ANY
 | 
					 | 
				
			||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 | 
					 | 
				
			||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 | 
					 | 
				
			||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 | 
					 | 
				
			||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
					 | 
				
			||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
					 | 
				
			||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
OrderedDict - A port of the Python 2.7+ OrderedDict to Python 2.6
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Copyright (c) 2009 Raymond Hettinger
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Permission is hereby granted, free of charge, to any person
 | 
					 | 
				
			||||||
obtaining a copy of this software and associated documentation files
 | 
					 | 
				
			||||||
(the "Software"), to deal in the Software without restriction,
 | 
					 | 
				
			||||||
including without limitation the rights to use, copy, modify, merge,
 | 
					 | 
				
			||||||
publish, distribute, sublicense, and/or sell copies of the Software,
 | 
					 | 
				
			||||||
and to permit persons to whom the Software is furnished to do so,
 | 
					 | 
				
			||||||
subject to the following conditions:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    The above copyright notice and this permission notice shall be
 | 
					 | 
				
			||||||
    included in all copies or substantial portions of the Software.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
					 | 
				
			||||||
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 | 
					 | 
				
			||||||
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | 
					 | 
				
			||||||
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 | 
					 | 
				
			||||||
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 | 
					 | 
				
			||||||
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
					 | 
				
			||||||
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 | 
					 | 
				
			||||||
    OTHER DEALINGS IN THE SOFTWARE.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SUELTA – A PURE-PYTHON SASL CLIENT LIBRARY
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This software is subject to "The MIT License"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Copyright 2004-2013 David Alan Cridland
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
					 | 
				
			||||||
of this software and associated documentation files (the "Software"), to deal
 | 
					 | 
				
			||||||
in the Software without restriction, including without limitation the rights
 | 
					 | 
				
			||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
					 | 
				
			||||||
copies of the Software, and to permit persons to whom the Software is
 | 
					 | 
				
			||||||
furnished to do so, subject to the following conditions:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The above copyright notice and this permission notice shall be included in
 | 
					 | 
				
			||||||
all copies or substantial portions of the Software.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
					 | 
				
			||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
					 | 
				
			||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
					 | 
				
			||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
					 | 
				
			||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
					 | 
				
			||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
					 | 
				
			||||||
THE SOFTWARE.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
python-gnupg: A Python wrapper for the GNU Privacy Guard
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
Copyright (c) 2008-2012 by Vinay Sajip.
 | 
					 | 
				
			||||||
All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Redistribution and use in source and binary forms, with or without
 | 
					 | 
				
			||||||
modification, are permitted provided that the following conditions are met:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    * Redistributions of source code must retain the above copyright notice,
 | 
					 | 
				
			||||||
      this list of conditions and the following disclaimer.
 | 
					 | 
				
			||||||
    * Redistributions in binary form must reproduce the above copyright notice,
 | 
					 | 
				
			||||||
      this list of conditions and the following disclaimer in the documentation
 | 
					 | 
				
			||||||
      and/or other materials provided with the distribution.
 | 
					 | 
				
			||||||
    * The name(s) of the copyright holder(s) may not be used to endorse or
 | 
					 | 
				
			||||||
      promote products derived from this software without specific prior
 | 
					 | 
				
			||||||
      written permission.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) "AS IS" AND ANY EXPRESS OR
 | 
					 | 
				
			||||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | 
					 | 
				
			||||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 | 
					 | 
				
			||||||
EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 | 
					 | 
				
			||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
					 | 
				
			||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
					 | 
				
			||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
					 | 
				
			||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 | 
					 | 
				
			||||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 | 
					 | 
				
			||||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
socksipy: A Python SOCKS client module.
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
Copyright 2006 Dan-Haim. All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Redistribution and use in source and binary forms, with or without modification,
 | 
					 | 
				
			||||||
are permitted provided that the following conditions are met:
 | 
					 | 
				
			||||||
1. Redistributions of source code must retain the above copyright notice, this
 | 
					 | 
				
			||||||
   list of conditions and the following disclaimer.
 | 
					 | 
				
			||||||
2. Redistributions in binary form must reproduce the above copyright notice,
 | 
					 | 
				
			||||||
   this list of conditions and the following disclaimer in the documentation
 | 
					 | 
				
			||||||
   and/or other materials provided with the distribution.
 | 
					 | 
				
			||||||
3. Neither the name of Dan Haim nor the names of his contributors may be used
 | 
					 | 
				
			||||||
   to endorse or promote products derived from this software without specific
 | 
					 | 
				
			||||||
   prior written permission.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED
 | 
					 | 
				
			||||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | 
					 | 
				
			||||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 | 
					 | 
				
			||||||
EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 | 
					 | 
				
			||||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
					 | 
				
			||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA
 | 
					 | 
				
			||||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
					 | 
				
			||||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 | 
					 | 
				
			||||||
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										39
									
								
								MANIFEST
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								MANIFEST
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					setup.py
 | 
				
			||||||
 | 
					sleekxmpp/__init__.py
 | 
				
			||||||
 | 
					sleekxmpp/basexmpp.py
 | 
				
			||||||
 | 
					sleekxmpp/clientxmpp.py
 | 
				
			||||||
 | 
					sleekxmpp/example.py
 | 
				
			||||||
 | 
					sleekxmpp/plugins/__init__.py
 | 
				
			||||||
 | 
					sleekxmpp/plugins/base.py
 | 
				
			||||||
 | 
					sleekxmpp/plugins/gmail_notify.py
 | 
				
			||||||
 | 
					sleekxmpp/plugins/xep_0004.py
 | 
				
			||||||
 | 
					sleekxmpp/plugins/xep_0009.py
 | 
				
			||||||
 | 
					sleekxmpp/plugins/xep_0030.py
 | 
				
			||||||
 | 
					sleekxmpp/plugins/xep_0045.py
 | 
				
			||||||
 | 
					sleekxmpp/plugins/xep_0050.py
 | 
				
			||||||
 | 
					sleekxmpp/plugins/xep_0060.py
 | 
				
			||||||
 | 
					sleekxmpp/plugins/xep_0078.py
 | 
				
			||||||
 | 
					sleekxmpp/plugins/xep_0086.py
 | 
				
			||||||
 | 
					sleekxmpp/plugins/xep_0092.py
 | 
				
			||||||
 | 
					sleekxmpp/plugins/xep_0199.py
 | 
				
			||||||
 | 
					sleekxmpp/stanza/__init__.py
 | 
				
			||||||
 | 
					sleekxmpp/stanza/iq.py
 | 
				
			||||||
 | 
					sleekxmpp/stanza/message.py
 | 
				
			||||||
 | 
					sleekxmpp/stanza/presence.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/__init__.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/stanzabase.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/statemachine.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/test.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/testclient.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/xmlstream.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/handler/__init__.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/handler/base.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/handler/callback.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/handler/waiter.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/handler/xmlcallback.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/handler/xmlwaiter.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/matcher/__init__.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/matcher/base.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/matcher/many.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/matcher/xmlmask.py
 | 
				
			||||||
 | 
					sleekxmpp/xmlstream/matcher/xpath.py
 | 
				
			||||||
@@ -1,7 +0,0 @@
 | 
				
			|||||||
include README.rst
 | 
					 | 
				
			||||||
include LICENSE
 | 
					 | 
				
			||||||
include run_tests.py
 | 
					 | 
				
			||||||
include slixmpp/stringprep.pyx
 | 
					 | 
				
			||||||
recursive-include docs Makefile *.bat *.py *.rst *.css *.ttf *.png
 | 
					 | 
				
			||||||
recursive-include examples *.py
 | 
					 | 
				
			||||||
recursive-include tests *.py
 | 
					 | 
				
			||||||
							
								
								
									
										42
									
								
								README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								README
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					SleekXMPP is an XMPP library written for Python 3.1+ (with 2.6 compatibility).
 | 
				
			||||||
 | 
					Hosted at http://wiki.github.com/fritzy/SleekXMPP/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Featured in examples in XMPP: The Definitive Guide by Kevin Smith, Remko Tronçon, and Peter Saint-Andre
 | 
				
			||||||
 | 
					If you're coming here from The Definitive Guide, please read http://wiki.github.com/fritzy/SleekXMPP/xmpp-the-definitive-guide
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SleekXMPP has several design goals/philosophies:
 | 
				
			||||||
 | 
					- Low number of dependencies.
 | 
				
			||||||
 | 
					- Every XEP as a plugin.
 | 
				
			||||||
 | 
					- Rewarding to work with.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The goals for 1.0 include (and we're getting close):
 | 
				
			||||||
 | 
					- Nearly Full test coverage of stanzas.
 | 
				
			||||||
 | 
					- Wide range of functional tests.
 | 
				
			||||||
 | 
					- Stanza objects for all interaction with the stream
 | 
				
			||||||
 | 
					- Documentation on using and extending SleekXMPP.
 | 
				
			||||||
 | 
					- Complete documentation on all implemented stanza objects
 | 
				
			||||||
 | 
					- Documentation on all examples used in XMPP: The Definitive Guide
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.1 will include:
 | 
				
			||||||
 | 
					- More functional and unit tests
 | 
				
			||||||
 | 
					- PEP-8 compliance
 | 
				
			||||||
 | 
					- XEP-225 support
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Since 0.2, here's the Changelog:
 | 
				
			||||||
 | 
					- MANY bugfixes
 | 
				
			||||||
 | 
					- Re-implementation of handlers/threading to greatly simplify and remove bugs (no more spawning threads in handlers)
 | 
				
			||||||
 | 
					- Stanza objects for jabber:client and all implemented XEPs
 | 
				
			||||||
 | 
					- Raising XMPPError for jabber:client and extended errors in handlers
 | 
				
			||||||
 | 
					- Robust error handling and better insurance of iq responses
 | 
				
			||||||
 | 
					- Stanza objects have made life a lot easier!
 | 
				
			||||||
 | 
					- Massive audit/cleanup.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Credits
 | 
				
			||||||
 | 
					----------------
 | 
				
			||||||
 | 
					Main Author: Nathan Fritz fritz@netflint.net
 | 
				
			||||||
 | 
					Contributors: Kevin Smith & Lance Stout
 | 
				
			||||||
 | 
					Patches: Remko Tronçon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Feel free to add fritzy@netflint.net to your roster for direct support and comments.
 | 
				
			||||||
 | 
					Join sleekxmpp-discussion@googlegroups.com / http://groups.google.com/group/sleekxmpp-discussion for email discussion.
 | 
				
			||||||
 | 
					Join sleek@conference.jabber.org for groupchat discussion.
 | 
				
			||||||
							
								
								
									
										121
									
								
								README.rst
									
									
									
									
									
								
							
							
						
						
									
										121
									
								
								README.rst
									
									
									
									
									
								
							@@ -1,121 +0,0 @@
 | 
				
			|||||||
Slixmpp
 | 
					 | 
				
			||||||
#########
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Slixmpp is an MIT licensed XMPP library for Python 3.4+. It is a fork of
 | 
					 | 
				
			||||||
SleekXMPP.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Slixmpp's goals is to only rewrite the core of the library (the low level
 | 
					 | 
				
			||||||
socket handling, the timers, the events dispatching) in order to remove all
 | 
					 | 
				
			||||||
threads.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Documentation and Testing
 | 
					 | 
				
			||||||
-------------------------
 | 
					 | 
				
			||||||
Documentation can be found both inline in the code, and as a Sphinx project in ``/docs``.
 | 
					 | 
				
			||||||
To generate the Sphinx documentation, follow the commands below. The HTML output will
 | 
					 | 
				
			||||||
be in ``docs/_build/html``::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cd docs
 | 
					 | 
				
			||||||
    make html
 | 
					 | 
				
			||||||
    open _build/html/index.html
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
To run the test suite for Slixmpp::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    python run_tests.py
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The Slixmpp Boilerplate
 | 
					 | 
				
			||||||
-------------------------
 | 
					 | 
				
			||||||
Projects using Slixmpp tend to follow a basic pattern for setting up client/component
 | 
					 | 
				
			||||||
connections and configuration. Here is the gist of the boilerplate needed for a Slixmpp
 | 
					 | 
				
			||||||
based project. See the documetation or examples directory for more detailed archetypes for
 | 
					 | 
				
			||||||
Slixmpp projects::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    import logging
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    from slixmpp import ClientXMPP
 | 
					 | 
				
			||||||
    from slixmpp.exceptions import IqError, IqTimeout
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class EchoBot(ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def __init__(self, jid, password):
 | 
					 | 
				
			||||||
            ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            self.add_event_handler("session_start", self.session_start)
 | 
					 | 
				
			||||||
            self.add_event_handler("message", self.message)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # If you wanted more functionality, here's how to register plugins:
 | 
					 | 
				
			||||||
            # self.register_plugin('xep_0030') # Service Discovery
 | 
					 | 
				
			||||||
            # self.register_plugin('xep_0199') # XMPP Ping
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Here's how to access plugins once you've registered them:
 | 
					 | 
				
			||||||
            # self['xep_0030'].add_feature('echo_demo')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # If you are working with an OpenFire server, you will
 | 
					 | 
				
			||||||
            # need to use a different SSL version:
 | 
					 | 
				
			||||||
            # import ssl
 | 
					 | 
				
			||||||
            # self.ssl_version = ssl.PROTOCOL_SSLv3
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def session_start(self, event):
 | 
					 | 
				
			||||||
            self.send_presence()
 | 
					 | 
				
			||||||
            self.get_roster()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Most get_*/set_* methods from plugins use Iq stanzas, which
 | 
					 | 
				
			||||||
            # can generate IqError and IqTimeout exceptions
 | 
					 | 
				
			||||||
            #
 | 
					 | 
				
			||||||
            # try:
 | 
					 | 
				
			||||||
            #     self.get_roster()
 | 
					 | 
				
			||||||
            # except IqError as err:
 | 
					 | 
				
			||||||
            #     logging.error('There was an error getting the roster')
 | 
					 | 
				
			||||||
            #     logging.error(err.iq['error']['condition'])
 | 
					 | 
				
			||||||
            #     self.disconnect()
 | 
					 | 
				
			||||||
            # except IqTimeout:
 | 
					 | 
				
			||||||
            #     logging.error('Server is taking too long to respond')
 | 
					 | 
				
			||||||
            #     self.disconnect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def message(self, msg):
 | 
					 | 
				
			||||||
            if msg['type'] in ('chat', 'normal'):
 | 
					 | 
				
			||||||
                msg.reply("Thanks for sending\n%(body)s" % msg).send()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if __name__ == '__main__':
 | 
					 | 
				
			||||||
        # Ideally use optparse or argparse to get JID,
 | 
					 | 
				
			||||||
        # password, and log level.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        logging.basicConfig(level=logging.DEBUG,
 | 
					 | 
				
			||||||
                            format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        xmpp = EchoBot('somejid@example.com', 'use_getpass')
 | 
					 | 
				
			||||||
        xmpp.connect()
 | 
					 | 
				
			||||||
        xmpp.process(forever=True)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Slixmpp Credits
 | 
					 | 
				
			||||||
---------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**Maintainer of the slixmpp fork:** Florent Le Coz
 | 
					 | 
				
			||||||
    `louiz@louiz.org <xmpp:louiz@louiz.org?message>`_,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Credits (SleekXMPP)
 | 
					 | 
				
			||||||
-------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**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 former 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>`_)
 | 
					 | 
				
			||||||
							
								
								
									
										171
									
								
								conn_tests/test_pubsubjobs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								conn_tests/test_pubsubjobs.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,171 @@
 | 
				
			|||||||
 | 
					import logging
 | 
				
			||||||
 | 
					import sleekxmpp
 | 
				
			||||||
 | 
					from optparse import OptionParser
 | 
				
			||||||
 | 
					from xml.etree import cElementTree as ET
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import unittest
 | 
				
			||||||
 | 
					import sleekxmpp.plugins.xep_0004
 | 
				
			||||||
 | 
					from sleekxmpp.xmlstream.matcher.stanzapath import StanzaPath
 | 
				
			||||||
 | 
					from sleekxmpp.xmlstream.handler.waiter import Waiter
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
						import configparser
 | 
				
			||||||
 | 
					except ImportError:
 | 
				
			||||||
 | 
						import ConfigParser as configparser
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
						import queue
 | 
				
			||||||
 | 
					except ImportError:
 | 
				
			||||||
 | 
						import Queue as queue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestClient(sleekxmpp.ClientXMPP):
 | 
				
			||||||
 | 
						def __init__(self, jid, password):
 | 
				
			||||||
 | 
							sleekxmpp.ClientXMPP.__init__(self, jid, password)
 | 
				
			||||||
 | 
							self.add_event_handler("session_start", self.start)
 | 
				
			||||||
 | 
							#self.add_event_handler("message", self.message)
 | 
				
			||||||
 | 
							self.waitforstart = queue.Queue()
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def start(self, event):
 | 
				
			||||||
 | 
							self.getRoster()
 | 
				
			||||||
 | 
							self.sendPresence()
 | 
				
			||||||
 | 
							self.waitforstart.put(True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestPubsubServer(unittest.TestCase):
 | 
				
			||||||
 | 
						statev = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
							unittest.TestCase.__init__(self, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def setUp(self):
 | 
				
			||||||
 | 
							pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def test001getdefaultconfig(self):
 | 
				
			||||||
 | 
							"""Get the default node config"""
 | 
				
			||||||
 | 
							self.xmpp1['xep_0060'].deleteNode(self.pshost, 'testnode2')
 | 
				
			||||||
 | 
							self.xmpp1['xep_0060'].deleteNode(self.pshost, 'testnode3')
 | 
				
			||||||
 | 
							self.xmpp1['xep_0060'].deleteNode(self.pshost, 'testnode4')
 | 
				
			||||||
 | 
							self.xmpp1['xep_0060'].deleteNode(self.pshost, 'testnode5')
 | 
				
			||||||
 | 
							result = self.xmpp1['xep_0060'].getNodeConfig(self.pshost)
 | 
				
			||||||
 | 
							self.statev['defaultconfig'] = result
 | 
				
			||||||
 | 
							self.failUnless(isinstance(result, sleekxmpp.plugins.xep_0004.Form))
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def test002createdefaultnode(self):
 | 
				
			||||||
 | 
							"""Create a node without config"""
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp1['xep_0060'].create_node(self.pshost, 'testnode1'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def test003deletenode(self):
 | 
				
			||||||
 | 
							"""Delete recently created node"""
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp1['xep_0060'].deleteNode(self.pshost, 'testnode1'))
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def test004createnode(self):
 | 
				
			||||||
 | 
							"""Create a node with a config"""
 | 
				
			||||||
 | 
							self.statev['defaultconfig'].field['pubsub#access_model'].setValue('open')
 | 
				
			||||||
 | 
							self.statev['defaultconfig'].field['pubsub#notify_retract'].setValue(True)
 | 
				
			||||||
 | 
							self.statev['defaultconfig'].field['pubsub#persist_items'].setValue(True)
 | 
				
			||||||
 | 
							self.statev['defaultconfig'].field['pubsub#presence_based_delivery'].setValue(True)
 | 
				
			||||||
 | 
							p = self.xmpp2.Presence()
 | 
				
			||||||
 | 
							p['to'] = self.pshost
 | 
				
			||||||
 | 
							p.send()
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp1['xep_0060'].create_node(self.pshost, 'testnode2', self.statev['defaultconfig'], ntype='job'))
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def test005reconfigure(self):
 | 
				
			||||||
 | 
							"""Retrieving node config and reconfiguring"""
 | 
				
			||||||
 | 
							nconfig = self.xmpp1['xep_0060'].getNodeConfig(self.pshost, 'testnode2')
 | 
				
			||||||
 | 
							self.failUnless(nconfig, "No configuration returned")
 | 
				
			||||||
 | 
							#print("\n%s ==\n %s" % (nconfig.getValues(), self.statev['defaultconfig'].getValues()))
 | 
				
			||||||
 | 
							self.failUnless(nconfig.getValues() == self.statev['defaultconfig'].getValues(), "Configuration does not match")
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp1['xep_0060'].setNodeConfig(self.pshost, 'testnode2', nconfig))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def test006subscribetonode(self):
 | 
				
			||||||
 | 
							"""Subscribe to node from account 2"""
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp2['xep_0060'].subscribe(self.pshost, "testnode2"))
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def test007publishitem(self):
 | 
				
			||||||
 | 
							"""Publishing item"""
 | 
				
			||||||
 | 
							item = ET.Element('{http://netflint.net/protocol/test}test')
 | 
				
			||||||
 | 
							w = Waiter('wait publish', StanzaPath('message/pubsub_event/items'))
 | 
				
			||||||
 | 
							self.xmpp2.registerHandler(w)
 | 
				
			||||||
 | 
							#result = self.xmpp1['xep_0060'].setItem(self.pshost, "testnode2", (('test1', item),))
 | 
				
			||||||
 | 
							result = self.xmpp1['jobs'].createJob(self.pshost, "testnode2", 'test1', item)
 | 
				
			||||||
 | 
							msg = w.wait(5) # got to get a result in 5 seconds
 | 
				
			||||||
 | 
							self.failUnless(msg != False, "Account #2 did not get message event")
 | 
				
			||||||
 | 
							#result = self.xmpp1['xep_0060'].setItem(self.pshost, "testnode2", (('test2', item),))
 | 
				
			||||||
 | 
							result = self.xmpp1['jobs'].createJob(self.pshost, "testnode2", 'test2', item)
 | 
				
			||||||
 | 
							w = Waiter('wait publish2', StanzaPath('message/pubsub_event/items'))
 | 
				
			||||||
 | 
							self.xmpp2.registerHandler(w)
 | 
				
			||||||
 | 
							self.xmpp2['jobs'].claimJob(self.pshost, 'testnode2', 'test1')
 | 
				
			||||||
 | 
							msg = w.wait(5) # got to get a result in 5 seconds
 | 
				
			||||||
 | 
							self.xmpp2['jobs'].claimJob(self.pshost, 'testnode2', 'test2')
 | 
				
			||||||
 | 
							self.xmpp2['jobs'].finishJob(self.pshost, 'testnode2', 'test1')
 | 
				
			||||||
 | 
							self.xmpp2['jobs'].finishJob(self.pshost, 'testnode2', 'test2')
 | 
				
			||||||
 | 
							print result
 | 
				
			||||||
 | 
							#need to add check for update
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def test900cleanup(self):
 | 
				
			||||||
 | 
							"Cleaning up"
 | 
				
			||||||
 | 
							#self.failUnless(self.xmpp1['xep_0060'].deleteNode(self.pshost, 'testnode2'), "Could not delete test node.")
 | 
				
			||||||
 | 
							time.sleep(10)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
						#parse command line arguements
 | 
				
			||||||
 | 
						optp = OptionParser()
 | 
				
			||||||
 | 
						optp.add_option('-q','--quiet', help='set logging to ERROR', action='store_const', dest='loglevel', const=logging.ERROR, default=logging.INFO)
 | 
				
			||||||
 | 
						optp.add_option('-d','--debug', help='set logging to DEBUG', action='store_const', dest='loglevel', const=logging.DEBUG, default=logging.INFO)
 | 
				
			||||||
 | 
						optp.add_option('-v','--verbose', help='set logging to COMM', action='store_const', dest='loglevel', const=5, default=logging.INFO)
 | 
				
			||||||
 | 
						optp.add_option("-c","--config", dest="configfile", default="config.xml", help="set config file to use")
 | 
				
			||||||
 | 
						optp.add_option("-n","--nodenum", dest="nodenum", default="1", help="set node number to use")
 | 
				
			||||||
 | 
						optp.add_option("-p","--pubsub", dest="pubsub", default="1", help="set pubsub host to use")
 | 
				
			||||||
 | 
						opts,args = optp.parse_args()
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						logging.basicConfig(level=opts.loglevel, format='%(levelname)-8s %(message)s')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#load xml config
 | 
				
			||||||
 | 
						logging.info("Loading config file: %s" % opts.configfile)
 | 
				
			||||||
 | 
						config = configparser.RawConfigParser()
 | 
				
			||||||
 | 
						config.read(opts.configfile)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						#init
 | 
				
			||||||
 | 
						logging.info("Account 1 is %s" % config.get('account1', 'jid'))
 | 
				
			||||||
 | 
						xmpp1 = TestClient(config.get('account1','jid'), config.get('account1','pass'))
 | 
				
			||||||
 | 
						logging.info("Account 2 is %s" % config.get('account2', 'jid'))
 | 
				
			||||||
 | 
						xmpp2 = TestClient(config.get('account2','jid'), config.get('account2','pass'))
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						xmpp1.registerPlugin('xep_0004')
 | 
				
			||||||
 | 
						xmpp1.registerPlugin('xep_0030')
 | 
				
			||||||
 | 
						xmpp1.registerPlugin('xep_0060')
 | 
				
			||||||
 | 
						xmpp1.registerPlugin('xep_0199')
 | 
				
			||||||
 | 
						xmpp1.registerPlugin('jobs')
 | 
				
			||||||
 | 
						xmpp2.registerPlugin('xep_0004')
 | 
				
			||||||
 | 
						xmpp2.registerPlugin('xep_0030')
 | 
				
			||||||
 | 
						xmpp2.registerPlugin('xep_0060')
 | 
				
			||||||
 | 
						xmpp2.registerPlugin('xep_0199')
 | 
				
			||||||
 | 
						xmpp2.registerPlugin('jobs')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if not config.get('account1', 'server'):
 | 
				
			||||||
 | 
							# we don't know the server, but the lib can probably figure it out
 | 
				
			||||||
 | 
							xmpp1.connect() 
 | 
				
			||||||
 | 
						else:
 | 
				
			||||||
 | 
							xmpp1.connect((config.get('account1', 'server'), 5222))
 | 
				
			||||||
 | 
						xmpp1.process(threaded=True)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						#init
 | 
				
			||||||
 | 
						if not config.get('account2', 'server'):
 | 
				
			||||||
 | 
							# we don't know the server, but the lib can probably figure it out
 | 
				
			||||||
 | 
							xmpp2.connect() 
 | 
				
			||||||
 | 
						else:
 | 
				
			||||||
 | 
							xmpp2.connect((config.get('account2', 'server'), 5222))
 | 
				
			||||||
 | 
						xmpp2.process(threaded=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TestPubsubServer.xmpp1 = xmpp1
 | 
				
			||||||
 | 
						TestPubsubServer.xmpp2 = xmpp2
 | 
				
			||||||
 | 
						TestPubsubServer.pshost = config.get('settings', 'pubsub')
 | 
				
			||||||
 | 
						xmpp1.waitforstart.get(True)
 | 
				
			||||||
 | 
						xmpp2.waitforstart.get(True)
 | 
				
			||||||
 | 
						testsuite = unittest.TestLoader().loadTestsFromTestCase(TestPubsubServer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						alltests_suite = unittest.TestSuite([testsuite])
 | 
				
			||||||
 | 
						result = unittest.TextTestRunner(verbosity=2).run(alltests_suite)
 | 
				
			||||||
 | 
						xmpp1.disconnect()
 | 
				
			||||||
 | 
						xmpp2.disconnect()
 | 
				
			||||||
							
								
								
									
										233
									
								
								conn_tests/test_pubsubserver.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										233
									
								
								conn_tests/test_pubsubserver.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,233 @@
 | 
				
			|||||||
 | 
					import logging
 | 
				
			||||||
 | 
					import sleekxmpp
 | 
				
			||||||
 | 
					from optparse import OptionParser
 | 
				
			||||||
 | 
					from xml.etree import cElementTree as ET
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import unittest
 | 
				
			||||||
 | 
					import sleekxmpp.plugins.xep_0004
 | 
				
			||||||
 | 
					from sleekxmpp.xmlstream.matcher.stanzapath import StanzaPath
 | 
				
			||||||
 | 
					from sleekxmpp.xmlstream.handler.waiter import Waiter
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
						import configparser
 | 
				
			||||||
 | 
					except ImportError:
 | 
				
			||||||
 | 
						import ConfigParser as configparser
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
						import queue
 | 
				
			||||||
 | 
					except ImportError:
 | 
				
			||||||
 | 
						import Queue as queue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestClient(sleekxmpp.ClientXMPP):
 | 
				
			||||||
 | 
						def __init__(self, jid, password):
 | 
				
			||||||
 | 
							sleekxmpp.ClientXMPP.__init__(self, jid, password)
 | 
				
			||||||
 | 
							self.add_event_handler("session_start", self.start)
 | 
				
			||||||
 | 
							#self.add_event_handler("message", self.message)
 | 
				
			||||||
 | 
							self.waitforstart = queue.Queue()
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def start(self, event):
 | 
				
			||||||
 | 
							self.getRoster()
 | 
				
			||||||
 | 
							self.sendPresence()
 | 
				
			||||||
 | 
							self.waitforstart.put(True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestPubsubServer(unittest.TestCase):
 | 
				
			||||||
 | 
						statev = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
							unittest.TestCase.__init__(self, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def setUp(self):
 | 
				
			||||||
 | 
							pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def test001getdefaultconfig(self):
 | 
				
			||||||
 | 
							"""Get the default node config"""
 | 
				
			||||||
 | 
							self.xmpp1['xep_0060'].deleteNode(self.pshost, 'testnode2')
 | 
				
			||||||
 | 
							self.xmpp1['xep_0060'].deleteNode(self.pshost, 'testnode3')
 | 
				
			||||||
 | 
							self.xmpp1['xep_0060'].deleteNode(self.pshost, 'testnode4')
 | 
				
			||||||
 | 
							self.xmpp1['xep_0060'].deleteNode(self.pshost, 'testnode5')
 | 
				
			||||||
 | 
							result = self.xmpp1['xep_0060'].getNodeConfig(self.pshost)
 | 
				
			||||||
 | 
							self.statev['defaultconfig'] = result
 | 
				
			||||||
 | 
							self.failUnless(isinstance(result, sleekxmpp.plugins.xep_0004.Form))
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def test002createdefaultnode(self):
 | 
				
			||||||
 | 
							"""Create a node without config"""
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp1['xep_0060'].create_node(self.pshost, 'testnode1'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def test003deletenode(self):
 | 
				
			||||||
 | 
							"""Delete recently created node"""
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp1['xep_0060'].deleteNode(self.pshost, 'testnode1'))
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def test004createnode(self):
 | 
				
			||||||
 | 
							"""Create a node with a config"""
 | 
				
			||||||
 | 
							self.statev['defaultconfig'].field['pubsub#access_model'].setValue('open')
 | 
				
			||||||
 | 
							self.statev['defaultconfig'].field['pubsub#notify_retract'].setValue(True)
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp1['xep_0060'].create_node(self.pshost, 'testnode2', self.statev['defaultconfig']))
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def test005reconfigure(self):
 | 
				
			||||||
 | 
							"""Retrieving node config and reconfiguring"""
 | 
				
			||||||
 | 
							nconfig = self.xmpp1['xep_0060'].getNodeConfig(self.pshost, 'testnode2')
 | 
				
			||||||
 | 
							self.failUnless(nconfig, "No configuration returned")
 | 
				
			||||||
 | 
							#print("\n%s ==\n %s" % (nconfig.getValues(), self.statev['defaultconfig'].getValues()))
 | 
				
			||||||
 | 
							self.failUnless(nconfig.getValues() == self.statev['defaultconfig'].getValues(), "Configuration does not match")
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp1['xep_0060'].setNodeConfig(self.pshost, 'testnode2', nconfig))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def test006subscribetonode(self):
 | 
				
			||||||
 | 
							"""Subscribe to node from account 2"""
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp2['xep_0060'].subscribe(self.pshost, "testnode2"))
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def test007publishitem(self):
 | 
				
			||||||
 | 
							"""Publishing item"""
 | 
				
			||||||
 | 
							item = ET.Element('{http://netflint.net/protocol/test}test')
 | 
				
			||||||
 | 
							w = Waiter('wait publish', StanzaPath('message/pubsub_event/items'))
 | 
				
			||||||
 | 
							self.xmpp2.registerHandler(w)
 | 
				
			||||||
 | 
							result = self.xmpp1['xep_0060'].setItem(self.pshost, "testnode2", (('test1', item),))
 | 
				
			||||||
 | 
							msg = w.wait(5) # got to get a result in 5 seconds
 | 
				
			||||||
 | 
							self.failUnless(msg != False, "Account #2 did not get message event")
 | 
				
			||||||
 | 
							self.failUnless(result)
 | 
				
			||||||
 | 
							#need to add check for update
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def test008updateitem(self):
 | 
				
			||||||
 | 
							"""Updating item"""
 | 
				
			||||||
 | 
							item = ET.Element('{http://netflint.net/protocol/test}test', {'someattr': 'hi there'})
 | 
				
			||||||
 | 
							w = Waiter('wait publish', StanzaPath('message/pubsub_event/items'))
 | 
				
			||||||
 | 
							self.xmpp2.registerHandler(w)
 | 
				
			||||||
 | 
							result = self.xmpp1['xep_0060'].setItem(self.pshost, "testnode2", (('test1', item),))
 | 
				
			||||||
 | 
							msg = w.wait(5) # got to get a result in 5 seconds
 | 
				
			||||||
 | 
							self.failUnless(msg != False, "Account #2 did not get message event")
 | 
				
			||||||
 | 
							self.failUnless(result)
 | 
				
			||||||
 | 
							#need to add check for update
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def test009deleteitem(self):
 | 
				
			||||||
 | 
							"""Deleting item"""
 | 
				
			||||||
 | 
							w = Waiter('wait retract', StanzaPath('message/pubsub_event/items@node=testnode2'))
 | 
				
			||||||
 | 
							self.xmpp2.registerHandler(w)
 | 
				
			||||||
 | 
							result = self.xmpp1['xep_0060'].deleteItem(self.pshost, "testnode2", "test1")
 | 
				
			||||||
 | 
							self.failUnless(result, "Got error when deleting item.")
 | 
				
			||||||
 | 
							msg = w.wait(1)
 | 
				
			||||||
 | 
							self.failUnless(msg != False, "Did not get retract notice.")
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def test010unsubscribenode(self):
 | 
				
			||||||
 | 
							"Unsubscribing Account #2"
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp2['xep_0060'].unsubscribe(self.pshost, "testnode2"), "Got error response when unsubscribing.")
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def test011createcollectionnode(self):
 | 
				
			||||||
 | 
							"Create a collection node w/ Account #2"
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp2['xep_0060'].create_node(self.pshost, "testnode3", self.statev['defaultconfig'], True), "Could not create collection node")
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def test012subscribecollection(self):
 | 
				
			||||||
 | 
							"Subscribe Account #1 to collection"
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp1['xep_0060'].subscribe(self.pshost, "testnode3"))
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def test013assignnodetocollection(self):
 | 
				
			||||||
 | 
							"Assign node to collection"
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp2['xep_0060'].addNodeToCollection(self.pshost, 'testnode2', 'testnode3'))
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def test014publishcollection(self):
 | 
				
			||||||
 | 
							"""Publishing item to collection child"""
 | 
				
			||||||
 | 
							item = ET.Element('{http://netflint.net/protocol/test}test')
 | 
				
			||||||
 | 
							w = Waiter('wait publish2', StanzaPath('message/pubsub_event/items@node=testnode2'))
 | 
				
			||||||
 | 
							self.xmpp1.registerHandler(w)
 | 
				
			||||||
 | 
							result = self.xmpp2['xep_0060'].setItem(self.pshost, "testnode2", (('test2', item),))
 | 
				
			||||||
 | 
							msg = w.wait(5) # got to get a result in 5 seconds
 | 
				
			||||||
 | 
							self.failUnless(msg != False, "Account #1 did not get message event: perhaps node was advertised incorrectly?")
 | 
				
			||||||
 | 
							self.failUnless(result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#	def test016speedtest(self):
 | 
				
			||||||
 | 
					#		"Uncached speed test"
 | 
				
			||||||
 | 
					#		import time
 | 
				
			||||||
 | 
					#		start = time.time()
 | 
				
			||||||
 | 
					#		for y in range(0, 50000, 1000):
 | 
				
			||||||
 | 
					#			start2 = time.time()
 | 
				
			||||||
 | 
					#			for x in range(y, y+1000):
 | 
				
			||||||
 | 
					#				self.failUnless(self.xmpp1['xep_0060'].subscribe(self.pshost, "testnode4", subscribee="testuser%s@whatever" % x))
 | 
				
			||||||
 | 
					#			print time.time() - start2
 | 
				
			||||||
 | 
					#		seconds = time.time() - start
 | 
				
			||||||
 | 
					#		print "--", seconds
 | 
				
			||||||
 | 
					#		print "---------"
 | 
				
			||||||
 | 
					#		time.sleep(15)
 | 
				
			||||||
 | 
					#		self.failUnless(self.xmpp1['xep_0060'].deleteNode(self.pshost, 'testnode4'), "Could not delete non-cached test node")
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					#	def test015speedtest(self):
 | 
				
			||||||
 | 
					#		"cached speed test"
 | 
				
			||||||
 | 
					#		result = self.xmpp1['xep_0060'].getNodeConfig(self.pshost)
 | 
				
			||||||
 | 
					#		self.statev['defaultconfig'] = result
 | 
				
			||||||
 | 
					#		self.statev['defaultconfig'].field['pubsub#node_type'].setValue("leaf")
 | 
				
			||||||
 | 
					#		self.statev['defaultconfig'].field['sleek#saveonchange'].setValue(True)
 | 
				
			||||||
 | 
					#		self.failUnless(self.xmpp1['xep_0060'].create_node(self.pshost, 'testnode4', self.statev['defaultconfig']))
 | 
				
			||||||
 | 
					#		self.statev['defaultconfig'].field['sleek#saveonchange'].setValue(False)
 | 
				
			||||||
 | 
					#		self.failUnless(self.xmpp1['xep_0060'].create_node(self.pshost, 'testnode5', self.statev['defaultconfig']))
 | 
				
			||||||
 | 
					#		start = time.time()
 | 
				
			||||||
 | 
					#		for y in range(0, 50000, 1000):
 | 
				
			||||||
 | 
					#			start2 = time.time()
 | 
				
			||||||
 | 
					#			for x in range(y, y+1000):
 | 
				
			||||||
 | 
					#				self.failUnless(self.xmpp1['xep_0060'].subscribe(self.pshost, "testnode5", subscribee="testuser%s@whatever" % x))
 | 
				
			||||||
 | 
					#			print time.time() - start2
 | 
				
			||||||
 | 
					#		seconds = time.time() - start
 | 
				
			||||||
 | 
					#		print "--", seconds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def test900cleanup(self):
 | 
				
			||||||
 | 
							"Cleaning up"
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp1['xep_0060'].deleteNode(self.pshost, 'testnode2'), "Could not delete test node.")
 | 
				
			||||||
 | 
							self.failUnless(self.xmpp1['xep_0060'].deleteNode(self.pshost, 'testnode3'), "Could not delete collection node")
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
						#parse command line arguements
 | 
				
			||||||
 | 
						optp = OptionParser()
 | 
				
			||||||
 | 
						optp.add_option('-q','--quiet', help='set logging to ERROR', action='store_const', dest='loglevel', const=logging.ERROR, default=logging.INFO)
 | 
				
			||||||
 | 
						optp.add_option('-d','--debug', help='set logging to DEBUG', action='store_const', dest='loglevel', const=logging.DEBUG, default=logging.INFO)
 | 
				
			||||||
 | 
						optp.add_option('-v','--verbose', help='set logging to COMM', action='store_const', dest='loglevel', const=5, default=logging.INFO)
 | 
				
			||||||
 | 
						optp.add_option("-c","--config", dest="configfile", default="config.xml", help="set config file to use")
 | 
				
			||||||
 | 
						optp.add_option("-n","--nodenum", dest="nodenum", default="1", help="set node number to use")
 | 
				
			||||||
 | 
						optp.add_option("-p","--pubsub", dest="pubsub", default="1", help="set pubsub host to use")
 | 
				
			||||||
 | 
						opts,args = optp.parse_args()
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						logging.basicConfig(level=opts.loglevel, format='%(levelname)-8s %(message)s')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#load xml config
 | 
				
			||||||
 | 
						logging.info("Loading config file: %s" % opts.configfile)
 | 
				
			||||||
 | 
						config = configparser.RawConfigParser()
 | 
				
			||||||
 | 
						config.read(opts.configfile)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						#init
 | 
				
			||||||
 | 
						logging.info("Account 1 is %s" % config.get('account1', 'jid'))
 | 
				
			||||||
 | 
						xmpp1 = TestClient(config.get('account1','jid'), config.get('account1','pass'))
 | 
				
			||||||
 | 
						logging.info("Account 2 is %s" % config.get('account2', 'jid'))
 | 
				
			||||||
 | 
						xmpp2 = TestClient(config.get('account2','jid'), config.get('account2','pass'))
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						xmpp1.registerPlugin('xep_0004')
 | 
				
			||||||
 | 
						xmpp1.registerPlugin('xep_0030')
 | 
				
			||||||
 | 
						xmpp1.registerPlugin('xep_0060')
 | 
				
			||||||
 | 
						xmpp1.registerPlugin('xep_0199')
 | 
				
			||||||
 | 
						xmpp2.registerPlugin('xep_0004')
 | 
				
			||||||
 | 
						xmpp2.registerPlugin('xep_0030')
 | 
				
			||||||
 | 
						xmpp2.registerPlugin('xep_0060')
 | 
				
			||||||
 | 
						xmpp2.registerPlugin('xep_0199')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if not config.get('account1', 'server'):
 | 
				
			||||||
 | 
							# we don't know the server, but the lib can probably figure it out
 | 
				
			||||||
 | 
							xmpp1.connect() 
 | 
				
			||||||
 | 
						else:
 | 
				
			||||||
 | 
							xmpp1.connect((config.get('account1', 'server'), 5222))
 | 
				
			||||||
 | 
						xmpp1.process(threaded=True)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						#init
 | 
				
			||||||
 | 
						if not config.get('account2', 'server'):
 | 
				
			||||||
 | 
							# we don't know the server, but the lib can probably figure it out
 | 
				
			||||||
 | 
							xmpp2.connect() 
 | 
				
			||||||
 | 
						else:
 | 
				
			||||||
 | 
							xmpp2.connect((config.get('account2', 'server'), 5222))
 | 
				
			||||||
 | 
						xmpp2.process(threaded=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TestPubsubServer.xmpp1 = xmpp1
 | 
				
			||||||
 | 
						TestPubsubServer.xmpp2 = xmpp2
 | 
				
			||||||
 | 
						TestPubsubServer.pshost = config.get('settings', 'pubsub')
 | 
				
			||||||
 | 
						xmpp1.waitforstart.get(True)
 | 
				
			||||||
 | 
						xmpp2.waitforstart.get(True)
 | 
				
			||||||
 | 
						testsuite = unittest.TestLoader().loadTestsFromTestCase(TestPubsubServer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						alltests_suite = unittest.TestSuite([testsuite])
 | 
				
			||||||
 | 
						result = unittest.TextTestRunner(verbosity=2).run(alltests_suite)
 | 
				
			||||||
 | 
						xmpp1.disconnect()
 | 
				
			||||||
 | 
						xmpp2.disconnect()
 | 
				
			||||||
							
								
								
									
										13
									
								
								conn_tests/testconfig.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								conn_tests/testconfig.ini
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					[settings]
 | 
				
			||||||
 | 
					enabled=true
 | 
				
			||||||
 | 
					pubsub=pubsub.recon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[account1]
 | 
				
			||||||
 | 
					jid=fritzy@recon
 | 
				
			||||||
 | 
					pass=testing123
 | 
				
			||||||
 | 
					server=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[account2]
 | 
				
			||||||
 | 
					jid=fritzy2@recon
 | 
				
			||||||
 | 
					pass=testing123
 | 
				
			||||||
 | 
					server=
 | 
				
			||||||
							
								
								
									
										1
									
								
								docs/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								docs/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1 +0,0 @@
 | 
				
			|||||||
_build/*
 | 
					 | 
				
			||||||
							
								
								
									
										130
									
								
								docs/Makefile
									
									
									
									
									
								
							
							
						
						
									
										130
									
								
								docs/Makefile
									
									
									
									
									
								
							@@ -1,130 +0,0 @@
 | 
				
			|||||||
# 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/Slixmpp.qhcp"
 | 
					 | 
				
			||||||
	@echo "To view the help file:"
 | 
					 | 
				
			||||||
	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Slixmpp.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/Slixmpp"
 | 
					 | 
				
			||||||
	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Slixmpp"
 | 
					 | 
				
			||||||
	@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."
 | 
					 | 
				
			||||||
							
								
								
									
										452
									
								
								docs/_static/agogo.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										452
									
								
								docs/_static/agogo.css
									
									
									
									
										vendored
									
									
								
							@@ -1,452 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * 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;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										532
									
								
								docs/_static/basic.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										532
									
								
								docs/_static/basic.css
									
									
									
									
										vendored
									
									
								
							@@ -1,532 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * 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;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										256
									
								
								docs/_static/default.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										256
									
								
								docs/_static/default.css
									
									
									
									
										vendored
									
									
								
							@@ -1,256 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * 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;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								docs/_static/fonts/Museo_Slab_500.otf
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/_static/fonts/Museo_Slab_500.otf
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								docs/_static/fonts/Museo_Slab_500italic.otf
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/_static/fonts/Museo_Slab_500italic.otf
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								docs/_static/fonts/OFLGoudyStMTT-Italic.ttf
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/_static/fonts/OFLGoudyStMTT-Italic.ttf
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								docs/_static/fonts/OFLGoudyStMTT.ttf
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/_static/fonts/OFLGoudyStMTT.ttf
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								docs/_static/fonts/YanoneKaffeesatz-Bold.ttf
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/_static/fonts/YanoneKaffeesatz-Bold.ttf
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								docs/_static/fonts/YanoneKaffeesatz-Light.ttf
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/_static/fonts/YanoneKaffeesatz-Light.ttf
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								docs/_static/fonts/YanoneKaffeesatz-Regular.ttf
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/_static/fonts/YanoneKaffeesatz-Regular.ttf
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								docs/_static/fonts/YanoneKaffeesatz-Thin.ttf
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/_static/fonts/YanoneKaffeesatz-Thin.ttf
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										431
									
								
								docs/_static/haiku.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										431
									
								
								docs/_static/haiku.css
									
									
									
									
										vendored
									
									
								
							@@ -1,431 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * 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: 30em;
 | 
					 | 
				
			||||||
    max-width: 70em;
 | 
					 | 
				
			||||||
    color: #444;
 | 
					 | 
				
			||||||
    text-align: center;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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: auto;
 | 
					 | 
				
			||||||
    margin-top: 20px;
 | 
					 | 
				
			||||||
    margin-bottom: 50px;
 | 
					 | 
				
			||||||
    font-size: 0.9em;
 | 
					 | 
				
			||||||
    width: 700px;
 | 
					 | 
				
			||||||
    text-align: left;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* heading and navigation */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
div.header {
 | 
					 | 
				
			||||||
    position: relative;
 | 
					 | 
				
			||||||
    margin: auto;
 | 
					 | 
				
			||||||
    margin-top: 125px;
 | 
					 | 
				
			||||||
    height: 85px;
 | 
					 | 
				
			||||||
    padding: 0 40px;
 | 
					 | 
				
			||||||
    font-family: "Yanone Kaffeesatz";
 | 
					 | 
				
			||||||
    text-align: left;
 | 
					 | 
				
			||||||
    width: 750px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
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: auto;
 | 
					 | 
				
			||||||
    margin-top: 0;
 | 
					 | 
				
			||||||
    margin-bottom: 0px;
 | 
					 | 
				
			||||||
    text-align: right;
 | 
					 | 
				
			||||||
    font-size: 0.8em;
 | 
					 | 
				
			||||||
    width: 750px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
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;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#from_andyet {
 | 
					 | 
				
			||||||
    -webkit-box-shadow: #CCC 0px 0px 3px;
 | 
					 | 
				
			||||||
    background: rgba(255, 255, 255, 1);
 | 
					 | 
				
			||||||
    bottom: 0px;
 | 
					 | 
				
			||||||
    right: 17px;
 | 
					 | 
				
			||||||
    padding: 3px 10px;
 | 
					 | 
				
			||||||
    position: fixed;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#from_andyet h2 {
 | 
					 | 
				
			||||||
    background-image: url("images/from_&yet.png");
 | 
					 | 
				
			||||||
    background-repeat: no-repeat;
 | 
					 | 
				
			||||||
    height: 29px;
 | 
					 | 
				
			||||||
    line-height: 0;
 | 
					 | 
				
			||||||
    text-indent: -9999em;
 | 
					 | 
				
			||||||
    width: 79px;
 | 
					 | 
				
			||||||
    margin-top: 0;
 | 
					 | 
				
			||||||
    margin: 0px;
 | 
					 | 
				
			||||||
    padding: 0px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								docs/_static/header.png
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/_static/header.png
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 16 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/_static/images/arch_layers.png
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/_static/images/arch_layers.png
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 27 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								docs/_static/images/from_&yet.png
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/_static/images/from_&yet.png
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 2.7 KiB  | 
							
								
								
									
										70
									
								
								docs/_static/ir_black.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										70
									
								
								docs/_static/ir_black.css
									
									
									
									
										vendored
									
									
								
							@@ -1,70 +0,0 @@
 | 
				
			|||||||
.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 */
 | 
					 | 
				
			||||||
							
								
								
									
										245
									
								
								docs/_static/nature.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										245
									
								
								docs/_static/nature.css
									
									
									
									
										vendored
									
									
								
							@@ -1,245 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * 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;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								docs/_static/noise_dk.png
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/_static/noise_dk.png
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 22 KiB  | 
							
								
								
									
										70
									
								
								docs/_static/pygments.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										70
									
								
								docs/_static/pygments.css
									
									
									
									
										vendored
									
									
								
							@@ -1,70 +0,0 @@
 | 
				
			|||||||
.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 */
 | 
					 | 
				
			||||||
							
								
								
									
										339
									
								
								docs/_static/sphinxdoc.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										339
									
								
								docs/_static/sphinxdoc.css
									
									
									
									
										vendored
									
									
								
							@@ -1,339 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * 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;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										70
									
								
								docs/_templates/layout.html
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										70
									
								
								docs/_templates/layout.html
									
									
									
									
										vendored
									
									
								
							@@ -1,70 +0,0 @@
 | 
				
			|||||||
{#
 | 
					 | 
				
			||||||
    haiku/layout.html
 | 
					 | 
				
			||||||
    ~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Sphinx layout template for the haiku theme.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
 | 
					 | 
				
			||||||
    :license: BSD, see LICENSE for details.
 | 
					 | 
				
			||||||
#}
 | 
					 | 
				
			||||||
{% extends "basic/layout.html" %}
 | 
					 | 
				
			||||||
{% set script_files = script_files + ['_static/theme_extras.js'] %}
 | 
					 | 
				
			||||||
{% set css_files = css_files + ['_static/print.css'] %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{# do not display relbars #}
 | 
					 | 
				
			||||||
{% block relbar1 %}{% endblock %}
 | 
					 | 
				
			||||||
{% block relbar2 %}{% endblock %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% macro nav() %}
 | 
					 | 
				
			||||||
        <p>
 | 
					 | 
				
			||||||
        {%- block haikurel1 %}
 | 
					 | 
				
			||||||
        {%- endblock %}
 | 
					 | 
				
			||||||
        {%- if prev %}
 | 
					 | 
				
			||||||
        «  <a href="{{ prev.link|e }}">{{ prev.title }}</a>
 | 
					 | 
				
			||||||
          ::  
 | 
					 | 
				
			||||||
        {%- endif %}
 | 
					 | 
				
			||||||
        <a class="uplink" href="{{ pathto(master_doc) }}">{{ _('Contents') }}</a>
 | 
					 | 
				
			||||||
        {%- if next %}
 | 
					 | 
				
			||||||
          ::  
 | 
					 | 
				
			||||||
        <a href="{{ next.link|e }}">{{ next.title }}</a>  »
 | 
					 | 
				
			||||||
        {%- endif %}
 | 
					 | 
				
			||||||
        {%- block haikurel2 %}
 | 
					 | 
				
			||||||
        {%- endblock %}
 | 
					 | 
				
			||||||
        </p>
 | 
					 | 
				
			||||||
{% endmacro %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% block content %}
 | 
					 | 
				
			||||||
      <div class="header">
 | 
					 | 
				
			||||||
        {%- block haikuheader %}
 | 
					 | 
				
			||||||
        {%- if theme_full_logo != "false" %}
 | 
					 | 
				
			||||||
        <a href="{{ pathto('index') }}">
 | 
					 | 
				
			||||||
          <img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
 | 
					 | 
				
			||||||
        </a>
 | 
					 | 
				
			||||||
        {%- else %}
 | 
					 | 
				
			||||||
        {%- if logo -%}
 | 
					 | 
				
			||||||
          <img class="rightlogo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
 | 
					 | 
				
			||||||
        {%- endif -%}
 | 
					 | 
				
			||||||
        <h1 class="heading"><a href="{{ pathto('index') }}">
 | 
					 | 
				
			||||||
          <span>{{ title|striptags }}</span></a></h1>
 | 
					 | 
				
			||||||
        <h2 class="heading"><span>{{ shorttitle|e }}</span></h2>
 | 
					 | 
				
			||||||
        {%- endif %}
 | 
					 | 
				
			||||||
        {%- endblock %}
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <div class="topnav">
 | 
					 | 
				
			||||||
      {{ nav() }}
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <div class="content">
 | 
					 | 
				
			||||||
        {#{%- if display_toc %}
 | 
					 | 
				
			||||||
        <div id="toc">
 | 
					 | 
				
			||||||
          <h3>Table Of Contents</h3>
 | 
					 | 
				
			||||||
          {{ toc }}
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        {%- endif %}#}
 | 
					 | 
				
			||||||
        {% block body %}{% endblock %}
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <div class="bottomnav">
 | 
					 | 
				
			||||||
      {{ nav() }}
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <a id="from_andyet" href="http://andyet.net"><h2>From &yet</h2></a>
 | 
					 | 
				
			||||||
{% endblock %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@@ -1,8 +0,0 @@
 | 
				
			|||||||
========
 | 
					 | 
				
			||||||
BaseXMPP
 | 
					 | 
				
			||||||
========
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. module:: slixmpp.basexmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: BaseXMPP
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
@@ -1,8 +0,0 @@
 | 
				
			|||||||
==========
 | 
					 | 
				
			||||||
ClientXMPP
 | 
					 | 
				
			||||||
==========
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. module:: slixmpp.clientxmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: ClientXMPP
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
@@ -1,8 +0,0 @@
 | 
				
			|||||||
=============
 | 
					 | 
				
			||||||
ComponentXMPP
 | 
					 | 
				
			||||||
=============
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. module:: slixmpp.componentxmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: ComponentXMPP
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
@@ -1,14 +0,0 @@
 | 
				
			|||||||
Exceptions
 | 
					 | 
				
			||||||
==========
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. module:: slixmpp.exceptions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoexception:: XMPPError
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoexception:: IqError
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoexception:: IqTimeout
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
@@ -1,8 +0,0 @@
 | 
				
			|||||||
IQ Stanza
 | 
					 | 
				
			||||||
=========
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. module:: slixmpp.stanza
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: Iq
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@@ -1,7 +0,0 @@
 | 
				
			|||||||
Message Stanza
 | 
					 | 
				
			||||||
==============
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. module:: slixmpp.stanza
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: Message
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
@@ -1,8 +0,0 @@
 | 
				
			|||||||
Presence Stanza
 | 
					 | 
				
			||||||
===============
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. module:: slixmpp.stanza
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: Presence
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@@ -1,8 +0,0 @@
 | 
				
			|||||||
Root Stanza
 | 
					 | 
				
			||||||
===========
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. module:: slixmpp.stanza.rootstanza
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: RootStanza
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@@ -1,28 +0,0 @@
 | 
				
			|||||||
Stanza Handlers
 | 
					 | 
				
			||||||
===============
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The Basic Handler
 | 
					 | 
				
			||||||
-----------------
 | 
					 | 
				
			||||||
.. module:: slixmpp.xmlstream.handler.base
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: BaseHandler
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Callback
 | 
					 | 
				
			||||||
--------
 | 
					 | 
				
			||||||
.. module:: slixmpp.xmlstream.handler
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: Callback
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
CoroutineCallback
 | 
					 | 
				
			||||||
-----------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: CoroutineCallback
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Waiter
 | 
					 | 
				
			||||||
------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: Waiter
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
@@ -1,7 +0,0 @@
 | 
				
			|||||||
Jabber IDs (JID)
 | 
					 | 
				
			||||||
=================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. module:: slixmpp.jid
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: JID
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
@@ -1,41 +0,0 @@
 | 
				
			|||||||
Stanza Matchers
 | 
					 | 
				
			||||||
===============
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The Basic Matcher
 | 
					 | 
				
			||||||
-----------------
 | 
					 | 
				
			||||||
.. module:: slixmpp.xmlstream.matcher.base
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: MatcherBase
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ID Matching
 | 
					 | 
				
			||||||
-----------
 | 
					 | 
				
			||||||
.. module:: slixmpp.xmlstream.matcher.id
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: MatcherId
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Stanza Path Matching
 | 
					 | 
				
			||||||
--------------------
 | 
					 | 
				
			||||||
.. module:: slixmpp.xmlstream.matcher.stanzapath
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: StanzaPath
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
XPath
 | 
					 | 
				
			||||||
-----
 | 
					 | 
				
			||||||
.. module:: slixmpp.xmlstream.matcher.xpath
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: MatchXPath
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
XMLMask
 | 
					 | 
				
			||||||
-------
 | 
					 | 
				
			||||||
.. module:: slixmpp.xmlstream.matcher.xmlmask
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: MatchXMLMask
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
@@ -1,123 +0,0 @@
 | 
				
			|||||||
.. _stanzabase:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
==============
 | 
					 | 
				
			||||||
Stanza Objects
 | 
					 | 
				
			||||||
==============
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. module:: slixmpp.xmlstream.stanzabase
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The :mod:`~slixmpp.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:`~slixmpp.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 Slixmpp 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 <substanza>`, 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="Slixmpp 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="Slixmpp Bot" />
 | 
					 | 
				
			||||||
    </query>'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
We can then drill down through the plugin object's interfaces as desired::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    >>> iq['disco_info']['identities']
 | 
					 | 
				
			||||||
    [('client', 'bot', 'Slixmpp 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:
 | 
					 | 
				
			||||||
@@ -1,47 +0,0 @@
 | 
				
			|||||||
.. module:: slixmpp.xmlstream.tostring
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. _tostring:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
XML Serialization
 | 
					 | 
				
			||||||
=================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Since the XML layer of Slixmpp 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 slixmpp.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:`~slixmpp.stanza.message.Message` instance and dump
 | 
					 | 
				
			||||||
it to the terminal, the ``jabber:client`` namespace will appear.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autofunction:: slixmpp.xmlstream.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: ``&``, ``<``, ``>``, ``'``, and ``"``.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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
 | 
					 | 
				
			||||||
@@ -1,8 +0,0 @@
 | 
				
			|||||||
==========
 | 
					 | 
				
			||||||
XML Stream
 | 
					 | 
				
			||||||
==========
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. module:: slixmpp.xmlstream.xmlstream
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: XMLStream
 | 
					 | 
				
			||||||
    :members:
 | 
					 | 
				
			||||||
@@ -1,150 +0,0 @@
 | 
				
			|||||||
.. index:: XMLStream, BaseXMPP, ClientXMPP, ComponentXMPP
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Slixmpp Architecture
 | 
					 | 
				
			||||||
======================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The core of Slixmpp 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:`~slixmpp.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 event loop
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
:class:`~slixmpp.xmlstream.xmlstream.XMLStream` instances inherit the
 | 
					 | 
				
			||||||
:class:`asyncio.BaseProtocol` class, and therefore do not have to handle
 | 
					 | 
				
			||||||
reads and writes directly, but receive data through
 | 
					 | 
				
			||||||
:meth:`~slixmpp.xmlstream.xmlstream.XMLStream.data_received` and write
 | 
					 | 
				
			||||||
data in the socket transport.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Upon receiving data, :term:`stream handlers <stream handler>` are run
 | 
					 | 
				
			||||||
immediately, except if they are coroutines, in which case they are
 | 
					 | 
				
			||||||
scheduled using :meth:`asyncio.async`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
:term:`Event handlers <event handler>` (which are called inside
 | 
					 | 
				
			||||||
:term:`stream handlers <stream handler>`) work the same way.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#. **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:`~slixmpp.stanza.Message`
 | 
					 | 
				
			||||||
   :term:`stanza object` because the namespaced element name
 | 
					 | 
				
			||||||
   ``{jabber:client}message`` is associated with the class
 | 
					 | 
				
			||||||
   :class:`~slixmpp.stanza.Message`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#. **Match stanza objects to callbacks.**
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   These objects are then compared against the stored patterns associated
 | 
					 | 
				
			||||||
   with the registered callback handlers.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   Each handler matching our :term:`stanza object` is then added to a list.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#. **Processing callbacks**
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   Every handler in the list is then called with the :term:`stanza object`
 | 
					 | 
				
			||||||
   as a parameter; if the handler is a
 | 
					 | 
				
			||||||
   :class:`~slixmpp.xmlstream.handler.CoroutineCallback`
 | 
					 | 
				
			||||||
   then it will be scheduled in the event loop using :meth:`asyncio.async`
 | 
					 | 
				
			||||||
   instead of run.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#. **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:`~slixmpp.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.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   In contrast to :term:`stream handlers <stream handler>`, these functions
 | 
					 | 
				
			||||||
   are referred to as :term:`event handlers <event handler>`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   The code for :meth:`BaseXMPP._handle_message` follows this pattern, and
 | 
					 | 
				
			||||||
   raises a ``'message'`` event
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   .. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.event('message', msg)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#. **Process Custom Events**
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   The :term:`event handlers <event handler>` are then executed, passing
 | 
					 | 
				
			||||||
   the stanza as the only argument.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
   .. 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::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
       reply = msg.reply()
 | 
					 | 
				
			||||||
       reply['body'] = "Hey! This is awesome!"
 | 
					 | 
				
			||||||
       reply.send()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. index:: BaseXMPP, XMLStream
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Raising XMPP Awareness: BaseXMPP
 | 
					 | 
				
			||||||
--------------------------------
 | 
					 | 
				
			||||||
While :class:`~slixmpp.xmlstream.xmlstream.XMLStream` attempts to shy away
 | 
					 | 
				
			||||||
from anything too XMPP specific, :class:`~slixmpp.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:`~slixmpp.basexmpp.BaseXMPP`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. index:: ClientXMPP, BaseXMPP
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ClientXMPP
 | 
					 | 
				
			||||||
----------
 | 
					 | 
				
			||||||
:class:`~slixmpp.clientxmpp.ClientXMPP` extends
 | 
					 | 
				
			||||||
:class:`~slixmpp.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:`~slixmpp.componentxmpp.ComponentXMPP` is only a thin layer on top of
 | 
					 | 
				
			||||||
:class:`~slixmpp.basexmpp.BaseXMPP` that implements the component handshake
 | 
					 | 
				
			||||||
protocol.
 | 
					 | 
				
			||||||
							
								
								
									
										222
									
								
								docs/conf.py
									
									
									
									
									
								
							
							
						
						
									
										222
									
								
								docs/conf.py
									
									
									
									
									
								
							@@ -1,222 +0,0 @@
 | 
				
			|||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
# Slixmpp 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'Slixmpp'
 | 
					 | 
				
			||||||
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.0'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 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 = 'haiku'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 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 = 'slixmpp'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 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 = 'Slixmppdoc'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# -- 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', 'Slixmpp.tex', u'Slixmpp 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', 'slixmpp', u'Slixmpp Documentation',
 | 
					 | 
				
			||||||
     [u'Nathan Fritz, Lance Stout'], 1)
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
intersphinx_mapping = {'python': ('http://docs.python.org/3.4', 'python-objects.inv')}
 | 
					 | 
				
			||||||
@@ -1,682 +0,0 @@
 | 
				
			|||||||
.. _create-plugin:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Creating a Slixmpp Plugin
 | 
					 | 
				
			||||||
===========================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
One of the goals of Slixmpp is to provide support for every draft or final
 | 
					 | 
				
			||||||
XMPP extension (`XEP <http://xmpp.org/extensions/>`_). To do this, Slixmpp 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 Slixmpp
 | 
					 | 
				
			||||||
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 Slixmpp
 | 
					 | 
				
			||||||
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:`BasePlugin <slixmpp.plugins.base.BasePlugin`,
 | 
					 | 
				
			||||||
and must include a ``plugin_init`` method. While the
 | 
					 | 
				
			||||||
plugins distributed with Slixmpp must be placed in the plugins directory
 | 
					 | 
				
			||||||
``slixmpp/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
 | 
					 | 
				
			||||||
``Slixmpp/slixmpp/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 Slixmpp Plugin
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This is a minimal implementation of XEP-0077 to serve
 | 
					 | 
				
			||||||
    as a tutorial for creating Slixmpp plugins.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    from slixmpp.plugins.base import BasePlugin
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class xep_0077(BasePlugin):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        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
 | 
					 | 
				
			||||||
``slixmpp/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 ``BasePlugin.post_init(self)``
 | 
					 | 
				
			||||||
which will mark that ``post_init`` has been called for the plugin. Once the
 | 
					 | 
				
			||||||
Slixmpp 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'``. Slixmpp
 | 
					 | 
				
			||||||
does not automatically load plugin dependencies for you.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def post_init(self):
 | 
					 | 
				
			||||||
        BasePlugin.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 Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* ``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 slixmpp.xmlstream import ElementBase, ET, JID, register_stanza_plugin
 | 
					 | 
				
			||||||
    from slixmpp 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
 | 
					 | 
				
			||||||
``slixmpp.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.register_handler(
 | 
					 | 
				
			||||||
          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().set_payload(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().set_payload(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().set_payload(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 slixmpp.componentxmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class Example(slixmpp.componentxmpp.ComponentXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def __init__(self, jid, password):
 | 
					 | 
				
			||||||
            slixmpp.componentxmpp.ComponentXMPP.__init__(self, jid, password, 'localhost', 8888)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            self.register_plugin('xep_0030')
 | 
					 | 
				
			||||||
            self.register_plugin('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.send_message(iq['from'], msg, mfrom=self.fulljid)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def unreg(self, iq):
 | 
					 | 
				
			||||||
            msg = "Bye! %s" % iq['register']['username']
 | 
					 | 
				
			||||||
            self.send_message(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 Slixmpp Plugin
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    This is a minimal implementation of XEP-0077 to serve
 | 
					 | 
				
			||||||
    as a tutorial for creating Slixmpp plugins.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    from slixmpp.plugins.base import BasePlugin
 | 
					 | 
				
			||||||
    from slixmpp.xmlstream.handler.callback import Callback
 | 
					 | 
				
			||||||
    from slixmpp.xmlstream.matcher.xpath import MatchXPath
 | 
					 | 
				
			||||||
    from slixmpp.xmlstream import ElementBase, ET, JID, register_stanza_plugin
 | 
					 | 
				
			||||||
    from slixmpp 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(BasePlugin):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        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.register_handler(
 | 
					 | 
				
			||||||
                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):
 | 
					 | 
				
			||||||
            BasePlugin.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)
 | 
					 | 
				
			||||||
                    reply = iq.reply()
 | 
					 | 
				
			||||||
                    reply.set_payload(iq['register'].xml)
 | 
					 | 
				
			||||||
                    reply.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)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            reply = iq.reply()
 | 
					 | 
				
			||||||
            reply.set_payload(reg.xml)
 | 
					 | 
				
			||||||
            reply.send()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def _sendError(self, iq, code, error_type, name, text=''):
 | 
					 | 
				
			||||||
            reply = iq.reply()
 | 
					 | 
				
			||||||
            reply.set_payload(iq['register'].xml)
 | 
					 | 
				
			||||||
            reply.error()
 | 
					 | 
				
			||||||
            reply['error']['code'] = code
 | 
					 | 
				
			||||||
            reply['error']['type'] = error_type
 | 
					 | 
				
			||||||
            reply['error']['condition'] = name
 | 
					 | 
				
			||||||
            reply['error']['text'] = text
 | 
					 | 
				
			||||||
            reply.send()
 | 
					 | 
				
			||||||
@@ -1,47 +0,0 @@
 | 
				
			|||||||
.. _differences:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Differences from SleekXMPP
 | 
					 | 
				
			||||||
==========================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**Python 3.4+ only**
 | 
					 | 
				
			||||||
    slixmpp will only work on python 3.4 and above.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**Stanza copies**
 | 
					 | 
				
			||||||
    The same stanza object is given through all the handlers; a handler that
 | 
					 | 
				
			||||||
    edits the stanza object should make its own copy.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**Replies**
 | 
					 | 
				
			||||||
    Because stanzas are not copied anymore,
 | 
					 | 
				
			||||||
    :meth:`Stanza.reply() <.StanzaBase.reply>` calls
 | 
					 | 
				
			||||||
    (for :class:`IQs <.Iq>`, :class:`Messages <.Message>`, etc)
 | 
					 | 
				
			||||||
    now return a new object instead of editing the stanza object
 | 
					 | 
				
			||||||
    in-place.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**Block and threaded arguments**
 | 
					 | 
				
			||||||
    All the functions that had a ``threaded=`` or ``block=`` argument
 | 
					 | 
				
			||||||
    do not have it anymore. Also, :meth:`.Iq.send` **does not block
 | 
					 | 
				
			||||||
    anymore**.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**Coroutine facilities**
 | 
					 | 
				
			||||||
    **See** :ref:`using_asyncio`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    If an event handler is a coroutine, it will be called asynchronously
 | 
					 | 
				
			||||||
    in the event loop instead of inside the event caller.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    A CoroutineCallback class has been added to create coroutine stream
 | 
					 | 
				
			||||||
    handlers, which will be also handled in the event loop.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    The :class:`~.slixmpp.stanza.Iq` object’s :meth:`~.slixmpp.stanza.Iq.send`
 | 
					 | 
				
			||||||
    method now **always** return a :class:`~.asyncio.Future` which result will be set
 | 
					 | 
				
			||||||
    to the IQ reply when it is received, or to ``None`` if the IQ is not of
 | 
					 | 
				
			||||||
    type ``get`` or ``set``.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Many plugins (WIP) calls which retrieve information also return the same
 | 
					 | 
				
			||||||
    future.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**Architectural differences**
 | 
					 | 
				
			||||||
    slixmpp does not have an event queue anymore, and instead processes
 | 
					 | 
				
			||||||
    handlers directly after receiving the XML stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. note::
 | 
					 | 
				
			||||||
    If you find something that doesn’t work but should, please report it.
 | 
					 | 
				
			||||||
@@ -1,282 +0,0 @@
 | 
				
			|||||||
Event Index
 | 
					 | 
				
			||||||
===========
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. glossary::
 | 
					 | 
				
			||||||
    :sorted:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    connected
 | 
					 | 
				
			||||||
        - **Data:** ``{}``
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.xmlstream.XMLstream`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Signal that a connection has been made with the XMPP server, but a session
 | 
					 | 
				
			||||||
        has not yet been established.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    connection_failed
 | 
					 | 
				
			||||||
        - **Data:** ``{}`` or ``Failure Stanza`` if available
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.xmlstream.XMLstream`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Signal that a connection can not be established after number of attempts.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    changed_status
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.Presence`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.roster.item.RosterItem`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        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:`~slixmpp.Presence`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.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 Slixmpp 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:`~slixmpp.plugins.xep_0030.stanza.DiscoInfo`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.plugins.xep_0030.disco.xep_0030`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Triggered whenever a ``disco#info`` result stanza is received.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    disco_items
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.plugins.xep_0030.stanza.DiscoItems`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.plugins.xep_0030.disco.xep_0030`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Triggered whenever a ``disco#items`` result stanza is received.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    disconnected
 | 
					 | 
				
			||||||
        - **Data:** ``{}``
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.xmlstream.XMLstream`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Signal that the connection with the XMPP server has been lost.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    entity_time
 | 
					 | 
				
			||||||
        - **Data:**
 | 
					 | 
				
			||||||
        - **Source:**
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    failed_auth
 | 
					 | 
				
			||||||
        - **Data:** ``{}``
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.ClientXMPP`, :py:class:`~slixmpp.plugins.xep_0078.xep_0078`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Signal that the server has rejected the provided login credentials.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    gmail_notify
 | 
					 | 
				
			||||||
        - **Data:** ``{}``
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.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:`~slixmpp.Iq`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.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:`~slixmpp.Presence`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.roster.item.RosterItem`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        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:`~slixmpp.Presence`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.roster.item.RosterItem`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Signal that an unavailable presence stanza has been received from a JID.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    groupchat_invite
 | 
					 | 
				
			||||||
        - **Data:**
 | 
					 | 
				
			||||||
        - **Source:**
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    groupchat_direct_invite
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.Message`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.plugins.xep_0249.direct`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    groupchat_message
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.Message`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.plugins.xep_0045.xep_0045`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Triggered whenever a message is received from a multi-user chat room.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    groupchat_presence
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.Presence`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.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:`~slixmpp.Message`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.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:`~slixmpp.Message`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`BaseXMPP <slixmpp.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_error
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.Message`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`BaseXMPP <slixmpp.BaseXMPP>`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Makes the contents of message stanzas available whenever one is received.
 | 
					 | 
				
			||||||
        Only handler messages with an ``error`` type.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    message_form
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.plugins.xep_0004.Form`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.plugins.xep_0004.xep_0004`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Currently the same as :term:`message_xform`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    message_xform
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.plugins.xep_0004.Form`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.plugins.xep_0004.xep_0004`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Triggered whenever a data form is received inside a message.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    muc::[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:`~slixmpp.Presence`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.BaseXMPP`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        A presence stanza with a type of '``available``' is received.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    presence_error
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.Presence`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.BaseXMPP`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        A presence stanza with a type of '``error``' is received.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    presence_form
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.plugins.xep_0004.Form`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.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:`~slixmpp.Presence`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.BaseXMPP`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        A presence stanza with a type of '``probe``' is received.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    presence_subscribe
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.Presence`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.BaseXMPP`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        A presence stanza with a type of '``subscribe``' is received.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    presence_subscribed
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.Presence`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.BaseXMPP`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        A presence stanza with a type of '``subscribed``' is received.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    presence_unavailable
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.Presence`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.BaseXMPP`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        A presence stanza with a type of '``unavailable``' is received.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    presence_unsubscribe
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.Presence`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.BaseXMPP`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        A presence stanza with a type of '``unsubscribe``' is received.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    presence_unsubscribed
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.Presence`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.BaseXMPP`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        A presence stanza with a type of '``unsubscribed``' is received.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    roster_update
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.stanza.Roster`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.ClientXMPP`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        An IQ result containing roster entries is received.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    sent_presence
 | 
					 | 
				
			||||||
        - **Data:** ``{}``
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.roster.multi.Roster`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Signal that an initial presence stanza has been written to the XML stream.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    session_end
 | 
					 | 
				
			||||||
        - **Data:** ``{}``
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.xmlstream.XMLstream`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        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 <slixmpp.ClientXMPP>`,
 | 
					 | 
				
			||||||
          :py:class:`ComponentXMPP <slixmpp.ComponentXMPP>`
 | 
					 | 
				
			||||||
          :py:class:`XEP-0078 <slixmpp.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:`~slixmpp.xmlstream.XMLstream`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    stream_error
 | 
					 | 
				
			||||||
        - **Data:** :py:class:`~slixmpp.stanza.StreamError`
 | 
					 | 
				
			||||||
        - **Source:** :py:class:`~slixmpp.BaseXMPP`
 | 
					 | 
				
			||||||
@@ -1,2 +0,0 @@
 | 
				
			|||||||
How to Use Stream Features
 | 
					 | 
				
			||||||
==========================
 | 
					 | 
				
			||||||
@@ -1,67 +0,0 @@
 | 
				
			|||||||
.. _echocomponent:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
=================================
 | 
					 | 
				
			||||||
Create and Run a Server Component
 | 
					 | 
				
			||||||
=================================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. note::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    If you have any issues working through this quickstart guide
 | 
					 | 
				
			||||||
    join the chat room at `slixmpp@muc.poez.io
 | 
					 | 
				
			||||||
    <xmpp:slixmpp@muc.poez.io?join>`_.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If you have not yet installed Slixmpp, do so now by either checking out a version
 | 
					 | 
				
			||||||
with `Git <http://git.poez.io/slixmpp>`_.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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 slixmpp.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.slixmpp.com', '******', 'slixmpp.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:`~slixmpp.basexmpp.BaseXMPP.send_message()` and
 | 
					 | 
				
			||||||
:meth:`~slixmpp.basexmpp.BaseXMPP.send_presence()` accept the parameters
 | 
					 | 
				
			||||||
``mfrom`` and ``pfrom``, respectively. For any method that uses
 | 
					 | 
				
			||||||
:class:`~slixmpp.stanza.iq.Iq` stanzas, ``ifrom`` may be used.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Final Product
 | 
					 | 
				
			||||||
-------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. include:: ../../examples/echo_component.py
 | 
					 | 
				
			||||||
    :literal:
 | 
					 | 
				
			||||||
@@ -1,349 +0,0 @@
 | 
				
			|||||||
.. _echobot:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
===============================
 | 
					 | 
				
			||||||
Slixmpp Quickstart - Echo Bot
 | 
					 | 
				
			||||||
===============================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. note::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    If you have any issues working through this quickstart guide
 | 
					 | 
				
			||||||
    join the chat room at `slixmpp@muc.poez.io
 | 
					 | 
				
			||||||
    <xmpp:slixmpp@muc.poez.io?join>`_.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If you have not yet installed Slixmpp, do so now by either checking out a version
 | 
					 | 
				
			||||||
with `Git <http://git.poez.io/slixmpp>`_.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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 asyncio
 | 
					 | 
				
			||||||
    import logging
 | 
					 | 
				
			||||||
    import getpass
 | 
					 | 
				
			||||||
    from optparse import OptionParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    import slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    '''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'''
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Slixmpp comes with a :class:`ClientXMPP <slixmpp.clientxmpp.ClientXMPP>` class
 | 
					 | 
				
			||||||
which we can extend to add our message echoing feature. :class:`ClientXMPP <slixmpp.clientxmpp.ClientXMPP>`
 | 
					 | 
				
			||||||
requires the parameters ``jid`` and ``password``, so we will let our ``EchoBot`` class accept those
 | 
					 | 
				
			||||||
as well.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class EchoBot(slixmpp.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 <slixmpp.basexmpp.BaseXMPP.send_presence>`.
 | 
					 | 
				
			||||||
Calling :meth:`send_presence <slixmpp.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 <slixmpp.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 <slixmpp.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
 | 
					 | 
				
			||||||
<slixmpp.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
 | 
					 | 
				
			||||||
<slixmpp.exceptions.IQTimeout>` is raised. Similarly, an :class:`IQError <slixmpp.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 <slixmpp.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. Slixmpp thus leaves out the ``from`` attribute when replying
 | 
					 | 
				
			||||||
    using a client connection.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Command Line Arguments and Logging
 | 
					 | 
				
			||||||
----------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
While this isn't part of Slixmpp 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 Slixmpp 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
 | 
					 | 
				
			||||||
``aiodns`` installed, then the :meth:`slixmpp.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 ``aiodns``, then Slixmpp will attempt to
 | 
					 | 
				
			||||||
connect to the hostname used by the JID, unless an address tuple is supplied
 | 
					 | 
				
			||||||
to :meth:`slixmpp.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')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
To begin responding to messages, you'll see we called :meth:`slixmpp.basexmpp.BaseXMPP.process`
 | 
					 | 
				
			||||||
which will start the event handling, send queue, and XML reader threads. It will also call
 | 
					 | 
				
			||||||
the :meth:`slixmpp.plugins.base.BasePlugin.post_init` method on all registered plugins. By
 | 
					 | 
				
			||||||
passing ``block=True`` to :meth:`slixmpp.basexmpp.BaseXMPP.process` we are running the
 | 
					 | 
				
			||||||
main processing loop in the main thread of execution. The :meth:`slixmpp.basexmpp.BaseXMPP.process`
 | 
					 | 
				
			||||||
call will not return until after Slixmpp 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:`slixmpp.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 Slixmpp 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 Slixmpp `examples directory <http://github.com/fritzy/Slixmpp/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:
 | 
					 | 
				
			||||||
@@ -1,182 +0,0 @@
 | 
				
			|||||||
Send/Receive IQ Stanzas
 | 
					 | 
				
			||||||
=======================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Unlike :class:`~slixmpp.stanza.message.Message` and
 | 
					 | 
				
			||||||
:class:`~slixmpp.stanza.presence.Presence` stanzas which only use
 | 
					 | 
				
			||||||
text data for basic usage, :class:`~slixmpp.stanza.iq.Iq` stanzas
 | 
					 | 
				
			||||||
require using XML payloads, and generally entail creating a new
 | 
					 | 
				
			||||||
Slixmpp plugin to provide the necessary convenience methods to
 | 
					 | 
				
			||||||
make working with them easier.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Basic Use
 | 
					 | 
				
			||||||
---------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
XMPP's use of :class:`~slixmpp.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
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Slixmpp provides built-in support for creating basic :class:`~slixmpp.stanza.iq.Iq`
 | 
					 | 
				
			||||||
stanzas this way. The relevant methods are:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq`
 | 
					 | 
				
			||||||
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq_get`
 | 
					 | 
				
			||||||
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq_set`
 | 
					 | 
				
			||||||
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq_result`
 | 
					 | 
				
			||||||
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq_error`
 | 
					 | 
				
			||||||
* :meth:`~slixmpp.basexmpp.BaseXMPP.make_iq_query`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
These methods all follow the same pattern: create or modify an existing
 | 
					 | 
				
			||||||
:class:`~slixmpp.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:`~slixmpp.stanza.iq.Iq` stanza is created, sending it
 | 
					 | 
				
			||||||
over the wire is done using its :meth:`~slixmpp.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:`~slixmpp.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:`~slixmpp.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:`~slixmpp.stanza.iq.Iq.send()`: :exc:`~slixmpp.exceptions.IqError`
 | 
					 | 
				
			||||||
and :exc:`~slixmpp.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:`~slixmpp.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 Slixmpp requires building at least a
 | 
					 | 
				
			||||||
rudimentary Slixmpp plugin to create a :term:`stanza object` for
 | 
					 | 
				
			||||||
interfacting with the :class:`~slixmpp.stanza.iq.Iq` payload.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. seealso::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    * :ref:`create-plugin`
 | 
					 | 
				
			||||||
    * :ref:`work-with-stanzas`
 | 
					 | 
				
			||||||
    * :ref:`using-handlers-matchers`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The typical way to respond to :class:`~slixmpp.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:`~slixmpp.xmlstream.stanzabase.ElementBase.plugin_attrib` value
 | 
					 | 
				
			||||||
of ``custom_xep``.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
There are two types of incoming :class:`~slixmpp.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:`~slixmpp.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')
 | 
					 | 
				
			||||||
@@ -1,200 +0,0 @@
 | 
				
			|||||||
.. _mucbot:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
=========================
 | 
					 | 
				
			||||||
Mulit-User Chat (MUC) Bot
 | 
					 | 
				
			||||||
=========================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. note::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    If you have any issues working through this quickstart guide
 | 
					 | 
				
			||||||
    join the chat room at `slixmpp@muc.poez.io
 | 
					 | 
				
			||||||
    <xmpp:slixmpp@muc.poez.io?join>`_.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
If you have not yet installed Slixmpp, do so now by either checking out a version
 | 
					 | 
				
			||||||
from `Git <http://git.poez.io/slixmpp>`_.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Now that you've got the basic gist of using Slixmpp by following the
 | 
					 | 
				
			||||||
echobot example (:ref:`echobot`), we can use one of the bundled plugins
 | 
					 | 
				
			||||||
to create a very popular XMPP starter project: a `Multi-User Chat`_
 | 
					 | 
				
			||||||
(MUC) bot. Our bot will login to an XMPP server, join an MUC chat room
 | 
					 | 
				
			||||||
and "lurk" indefinitely, responding with a generic message to anyone
 | 
					 | 
				
			||||||
that mentions its nickname. It will also greet members as they join the
 | 
					 | 
				
			||||||
chat room.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. _`multi-user chat`: http://xmpp.org/extensions/xep-0045.html
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Joining The Room
 | 
					 | 
				
			||||||
----------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
As usual, our code will be based on the pattern explained in :ref:`echobot`.
 | 
					 | 
				
			||||||
To start, we create an ``MUCBot`` class based on
 | 
					 | 
				
			||||||
:class:`ClientXMPP <slixmpp.clientxmpp.ClientXMPP>` and which accepts
 | 
					 | 
				
			||||||
parameters for the JID of the MUC room to join, and the nick that the
 | 
					 | 
				
			||||||
bot will use inside the chat room.  We also register an
 | 
					 | 
				
			||||||
:term:`event handler` for the :term:`session_start` event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    import slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class MUCBot(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def __init__(self, jid, password, room, nick):
 | 
					 | 
				
			||||||
            slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            self.room = room
 | 
					 | 
				
			||||||
            self.nick = nick
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
After initialization, we also need to register the MUC (XEP-0045) plugin
 | 
					 | 
				
			||||||
so that we can make use of the group chat plugin's methods and events.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0045')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Finally, we can make our bot join the chat room once an XMPP session
 | 
					 | 
				
			||||||
has been established:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.plugin['xep_0045'].joinMUC(self.room,
 | 
					 | 
				
			||||||
                                        self.nick,
 | 
					 | 
				
			||||||
                                        wait=True)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Note that as in :ref:`echobot`, we need to include send an initial presence and request
 | 
					 | 
				
			||||||
the roster. Next, we want to join the group chat, so we call the
 | 
					 | 
				
			||||||
``joinMUC`` method of the MUC plugin.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. note::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    The :attr:`plugin <slixmpp.basexmpp.BaseXMPP.plugin>` attribute is
 | 
					 | 
				
			||||||
    dictionary that maps to instances of plugins that we have previously
 | 
					 | 
				
			||||||
    registered, by their names.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Adding Functionality
 | 
					 | 
				
			||||||
--------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Currently, our bot just sits dormantly inside the chat room, but we
 | 
					 | 
				
			||||||
would like it to respond to two distinct events by issuing a generic
 | 
					 | 
				
			||||||
message in each case to the chat room. In particular, when a member
 | 
					 | 
				
			||||||
mentions the bot's nickname inside the chat room, and when a member
 | 
					 | 
				
			||||||
joins the chat room.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Responding to Mentions
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Whenever a user mentions our bot's nickname in chat, our bot will
 | 
					 | 
				
			||||||
respond with a generic message resembling *"I heard that, user."* We do
 | 
					 | 
				
			||||||
this by examining all of the messages sent inside the chat and looking
 | 
					 | 
				
			||||||
for the ones which contain the nickname string.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
First, we register an event handler for the :term:`groupchat_message`
 | 
					 | 
				
			||||||
event inside the bot's ``__init__`` function.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. note::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    We do not register a handler for the :term:`message` event in this
 | 
					 | 
				
			||||||
    bot, but if we did, the group chat message would have been sent to
 | 
					 | 
				
			||||||
    both handlers.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password, room, nick):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.room = room
 | 
					 | 
				
			||||||
        self.nick = nick
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
        self.add_event_handler("groupchat_message", self.muc_message)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Then, we can send our generic message whenever the bot's nickname gets
 | 
					 | 
				
			||||||
mentioned.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. warning::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Always check that a message is not from yourself,
 | 
					 | 
				
			||||||
    otherwise you will create an infinite loop responding
 | 
					 | 
				
			||||||
    to your own messages.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def muc_message(self, msg):
 | 
					 | 
				
			||||||
        if msg['mucnick'] != self.nick and self.nick in msg['body']:
 | 
					 | 
				
			||||||
            self.send_message(mto=msg['from'].bare,
 | 
					 | 
				
			||||||
                              mbody="I heard that, %s." % msg['mucnick'],
 | 
					 | 
				
			||||||
                              mtype='groupchat')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Greeting Members
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Now we want to greet member whenever they join the group chat. To
 | 
					 | 
				
			||||||
do this we will use the dynamic ``muc::room@server::got_online`` [1]_
 | 
					 | 
				
			||||||
event so it's a good idea to register an event handler for it.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. note::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    The groupchat_presence event is triggered whenever a
 | 
					 | 
				
			||||||
    presence stanza is received from any chat room, including
 | 
					 | 
				
			||||||
    any presences you send yourself. To limit event handling
 | 
					 | 
				
			||||||
    to a single room, use the events ``muc::room@server::presence``,
 | 
					 | 
				
			||||||
    ``muc::room@server::got_online``, or ``muc::room@server::got_offline``.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password, room, nick):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.room = room
 | 
					 | 
				
			||||||
        self.nick = nick
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
        self.add_event_handler("groupchat_message", self.muc_message)
 | 
					 | 
				
			||||||
        self.add_event_handler("muc::%s::got_online" % self.room,
 | 
					 | 
				
			||||||
                               self.muc_online)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Now all that's left to do is to greet them:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def muc_online(self, presence):
 | 
					 | 
				
			||||||
        if presence['muc']['nick'] != self.nick:
 | 
					 | 
				
			||||||
            self.send_message(mto=presence['from'].bare,
 | 
					 | 
				
			||||||
                              mbody="Hello, %s %s" % (presence['muc']['role'],
 | 
					 | 
				
			||||||
                                                      presence['muc']['nick']),
 | 
					 | 
				
			||||||
                              mtype='groupchat')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. [1] this is similar to the :term:`got_online` event and is sent by
 | 
					 | 
				
			||||||
       the xep_0045 plugin whenever a member joins the referenced
 | 
					 | 
				
			||||||
       MUC chat room.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Final Product
 | 
					 | 
				
			||||||
-------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. compound::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    The final step is to create a small runner script for initialising our ``MUCBot`` 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 muc.py -d -j jid@example.com -r room@muc.example.net -n lurkbot
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    which will prompt for the password, log in, and join the group chat. To test, open
 | 
					 | 
				
			||||||
    your regular IM client and join the same group chat that you sent the bot to. You
 | 
					 | 
				
			||||||
    will see ``lurkbot`` as one of the members in the group chat, and that it greeted
 | 
					 | 
				
			||||||
    you upon entry. Send a message with the string "lurkbot" inside the body text, and you
 | 
					 | 
				
			||||||
    will also see that it responds with our pre-programmed customized message.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. include:: ../../examples/muc.py
 | 
					 | 
				
			||||||
    :literal:
 | 
					 | 
				
			||||||
@@ -1,2 +0,0 @@
 | 
				
			|||||||
Manage Presence Subscriptions
 | 
					 | 
				
			||||||
=============================
 | 
					 | 
				
			||||||
@@ -1,40 +0,0 @@
 | 
				
			|||||||
.. _proxy:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
=========================
 | 
					 | 
				
			||||||
Enable HTTP Proxy Support
 | 
					 | 
				
			||||||
=========================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. note::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    If you have any issues working through this quickstart guide
 | 
					 | 
				
			||||||
    join the chat room at `slixmpp@muc.poez.io
 | 
					 | 
				
			||||||
    <xmpp:slixmpp@muc.poez.io?join>`_.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
In some instances, you may wish to route XMPP traffic through
 | 
					 | 
				
			||||||
an HTTP proxy, probably to get around restrictive firewalls.
 | 
					 | 
				
			||||||
Slixmpp provides support for basic HTTP proxying with DIGEST
 | 
					 | 
				
			||||||
authentication.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Enabling proxy support is done in two steps. The first is to instruct Slixmpp
 | 
					 | 
				
			||||||
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:
 | 
					 | 
				
			||||||
@@ -1,2 +0,0 @@
 | 
				
			|||||||
Send a Message Every 5 Minutes
 | 
					 | 
				
			||||||
==============================
 | 
					 | 
				
			||||||
@@ -1,92 +0,0 @@
 | 
				
			|||||||
Sign in, Send a Message, and Disconnect
 | 
					 | 
				
			||||||
=======================================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. note::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    If you have any issues working through this quickstart guide
 | 
					 | 
				
			||||||
    join the chat room at `slixmpp@muc.poez.io
 | 
					 | 
				
			||||||
    <xmpp:slixmpp@muc.poez.io?join>`_.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
A common use case for Slixmpp 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 <slixmpp.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 slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class SendMsgBot(slixmpp.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 <slixmpp.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 <slixmpp.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 <slixmpp.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) <slixmpp.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) <slixmpp.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:
 | 
					 | 
				
			||||||
@@ -1,34 +0,0 @@
 | 
				
			|||||||
.. _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 :class:`Matcher <.MatcherBase>` object, and
 | 
					 | 
				
			||||||
        which provides additional semantics. For example, the
 | 
					 | 
				
			||||||
        :class:`.Waiter` handler wrapper blocks thread execution until a
 | 
					 | 
				
			||||||
        matching stanza is received.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    event handler
 | 
					 | 
				
			||||||
        A callback function that responds to events raised by
 | 
					 | 
				
			||||||
        :meth:`.XMLStream.event`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    stanza object
 | 
					 | 
				
			||||||
        Informally may refer both to classes which extend :class:`.ElementBase`
 | 
					 | 
				
			||||||
        or :class:`.StanzaBase`, and to objects of such classes.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        A stanza object is a wrapper for an XML object which exposes :class:`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`
 | 
					 | 
				
			||||||
@@ -1,200 +0,0 @@
 | 
				
			|||||||
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 Slixmpp.) Slixmpp 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='Slixmpp',
 | 
					 | 
				
			||||||
                                  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``.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Performing 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 = yield from self['xep_0030'].get_info(jid='foo@example.com',
 | 
					 | 
				
			||||||
                                                node='bar',
 | 
					 | 
				
			||||||
                                                ifrom='baz@mycomponent.example.com',
 | 
					 | 
				
			||||||
                                                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)
 | 
					 | 
				
			||||||
@@ -1,4 +0,0 @@
 | 
				
			|||||||
.. _using-handlers-matchers:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Using Stream Handlers and Matchers
 | 
					 | 
				
			||||||
==================================
 | 
					 | 
				
			||||||
@@ -1,30 +0,0 @@
 | 
				
			|||||||
.. _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
 | 
					 | 
				
			||||||
--------------------------
 | 
					 | 
				
			||||||
							
								
								
									
										236
									
								
								docs/index.rst
									
									
									
									
									
								
							
							
						
						
									
										236
									
								
								docs/index.rst
									
									
									
									
									
								
							@@ -1,236 +0,0 @@
 | 
				
			|||||||
Slixmpp
 | 
					 | 
				
			||||||
#########
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. sidebar:: Get the Code
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    The latest source code for Slixmpp may be found on the `Git repo
 | 
					 | 
				
			||||||
    <http://git.poez.io/slixmpp>`_. ::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        git clone git://git.poez.io/slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    An XMPP chat room is available for discussing and getting help with slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    **Chat**
 | 
					 | 
				
			||||||
        `slixmpp@muc.poez.io <xmpp:slixmpp@muc.poez.io?join>`_
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    **Reporting bugs**
 | 
					 | 
				
			||||||
        You can report bugs at http://dev.louiz.org/projects/slixmpp/issues.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. note::
 | 
					 | 
				
			||||||
    slixmpp is a friendly fork of `SleekXMPP <https://github.com/fritzy/SleekXMPP>`_
 | 
					 | 
				
			||||||
    which goal is to use asyncio instead of threads to handle networking. See
 | 
					 | 
				
			||||||
    :ref:`differences`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Slixmpp is an :ref:`MIT licensed <license>` XMPP library for Python 3.4+,
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Slixmpp's design goals and philosphy are:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**Low number of dependencies**
 | 
					 | 
				
			||||||
    Installing and using Slixmpp 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 Slixmpp 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, Slixmpp 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.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Here's your first Slixmpp Bot:
 | 
					 | 
				
			||||||
--------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    import asyncio
 | 
					 | 
				
			||||||
    import logging
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    from slixmpp import ClientXMPP
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class EchoBot(ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def __init__(self, jid, password):
 | 
					 | 
				
			||||||
            ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            self.add_event_handler("session_start", self.session_start)
 | 
					 | 
				
			||||||
            self.add_event_handler("message", self.message)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # If you wanted more functionality, here's how to register plugins:
 | 
					 | 
				
			||||||
            # self.register_plugin('xep_0030') # Service Discovery
 | 
					 | 
				
			||||||
            # self.register_plugin('xep_0199') # XMPP Ping
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Here's how to access plugins once you've registered them:
 | 
					 | 
				
			||||||
            # self['xep_0030'].add_feature('echo_demo')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def session_start(self, event):
 | 
					 | 
				
			||||||
            self.send_presence()
 | 
					 | 
				
			||||||
            self.get_roster()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # Most get_*/set_* methods from plugins use Iq stanzas, which
 | 
					 | 
				
			||||||
            # are sent asynchronously. You can almost always provide a
 | 
					 | 
				
			||||||
            # callback that will be executed when the reply is received.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def message(self, msg):
 | 
					 | 
				
			||||||
            if msg['type'] in ('chat', 'normal'):
 | 
					 | 
				
			||||||
                msg.reply("Thanks for sending\n%(body)s" % msg).send()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if __name__ == '__main__':
 | 
					 | 
				
			||||||
        # Ideally use optparse or argparse to get JID,
 | 
					 | 
				
			||||||
        # password, and log level.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        logging.basicConfig(level=logging.DEBUG,
 | 
					 | 
				
			||||||
                            format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        xmpp = EchoBot('somejid@example.com', 'use_getpass')
 | 
					 | 
				
			||||||
        xmpp.connect()
 | 
					 | 
				
			||||||
        xmpp.process()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
To read if you come from SleekXMPP
 | 
					 | 
				
			||||||
----------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. toctree::
 | 
					 | 
				
			||||||
    :maxdepth: 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    differences
 | 
					 | 
				
			||||||
    using_asyncio
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    xeps
 | 
					 | 
				
			||||||
    xmpp_tdg
 | 
					 | 
				
			||||||
    howto/stanzas
 | 
					 | 
				
			||||||
    create_plugin
 | 
					 | 
				
			||||||
    features
 | 
					 | 
				
			||||||
    sasl
 | 
					 | 
				
			||||||
    handlersmatchers
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Plugin Guides
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
.. toctree::
 | 
					 | 
				
			||||||
    :maxdepth: 1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    guide_xep_0030
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Slixmpp 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/tostring
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Core Stanzas
 | 
					 | 
				
			||||||
~~~~~~~~~~~~
 | 
					 | 
				
			||||||
.. toctree::
 | 
					 | 
				
			||||||
    :maxdepth: 2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    api/stanza/rootstanza
 | 
					 | 
				
			||||||
    api/stanza/message
 | 
					 | 
				
			||||||
    api/stanza/presence
 | 
					 | 
				
			||||||
    api/stanza/iq
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Plugins
 | 
					 | 
				
			||||||
~~~~~~~
 | 
					 | 
				
			||||||
.. toctree::
 | 
					 | 
				
			||||||
    :maxdepth: 2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Additional Info
 | 
					 | 
				
			||||||
---------------
 | 
					 | 
				
			||||||
.. toctree::
 | 
					 | 
				
			||||||
    :hidden:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    glossary
 | 
					 | 
				
			||||||
    license
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* :ref:`license`
 | 
					 | 
				
			||||||
* :ref:`glossary`
 | 
					 | 
				
			||||||
* :ref:`genindex`
 | 
					 | 
				
			||||||
* :ref:`modindex`
 | 
					 | 
				
			||||||
* :ref:`search`
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SleekXMPP Credits
 | 
					 | 
				
			||||||
-----------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. note::
 | 
					 | 
				
			||||||
    Those people made SleekXMPP, so you should not bother them if
 | 
					 | 
				
			||||||
    you have an issue with slixmpp. But it’s still fair to credit
 | 
					 | 
				
			||||||
    them for their work.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**Main Author:** `Nathan Fritz <http://andyet.net/team/fritzy>`_
 | 
					 | 
				
			||||||
     `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 former member of the XMPP
 | 
					 | 
				
			||||||
     Council.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**Co-Author:** `Lance Stout <http://andyet.net/team/lance>`_
 | 
					 | 
				
			||||||
     `lancestout@gmail.com <xmpp:lancestout@gmail.com?message>`_,
 | 
					 | 
				
			||||||
     `@lancestout <http://twitter.com/lancestout>`_
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Both Fritzy and Lance work for `&yet <http://andyet.net>`_, which specializes in
 | 
					 | 
				
			||||||
realtime web and XMPP applications.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    - `contact@andyet.net <mailto:contact@andyet.net>`_
 | 
					 | 
				
			||||||
    - `XMPP Consulting <http://xmppconsulting.com>`_
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
**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>`_)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@@ -1,5 +0,0 @@
 | 
				
			|||||||
.. _license:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
License (MIT)
 | 
					 | 
				
			||||||
=============
 | 
					 | 
				
			||||||
.. include:: ../LICENSE
 | 
					 | 
				
			||||||
							
								
								
									
										170
									
								
								docs/make.bat
									
									
									
									
									
								
							
							
						
						
									
										170
									
								
								docs/make.bat
									
									
									
									
									
								
							@@ -1,170 +0,0 @@
 | 
				
			|||||||
@ECHO OFF
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
REM Command file for Sphinx documentation
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if "%SPHINXBUILD%" == "" (
 | 
					 | 
				
			||||||
	set SPHINXBUILD=sphinx-build
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
set BUILDDIR=_build
 | 
					 | 
				
			||||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
 | 
					 | 
				
			||||||
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\Slixmpp.qhcp
 | 
					 | 
				
			||||||
	echo.To view the help file:
 | 
					 | 
				
			||||||
	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Slixmpp.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
 | 
					 | 
				
			||||||
@@ -1,2 +0,0 @@
 | 
				
			|||||||
Plugin Architecture
 | 
					 | 
				
			||||||
===================
 | 
					 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							@@ -1,2 +0,0 @@
 | 
				
			|||||||
How SASL Authentication Works
 | 
					 | 
				
			||||||
=============================
 | 
					 | 
				
			||||||
@@ -1,148 +0,0 @@
 | 
				
			|||||||
.. _using_asyncio:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
=============
 | 
					 | 
				
			||||||
Using asyncio
 | 
					 | 
				
			||||||
=============
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Block on IQ sending
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
:meth:`.Iq.send` now returns a :class:`~.Future` so you can easily block with:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    result = yield from iq.send()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. warning::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    If the reply is an IQ with an ``error`` type, this will raise an
 | 
					 | 
				
			||||||
    :class:`.IqError`, and if it timeouts, it will raise an
 | 
					 | 
				
			||||||
    :class:`.IqTimeout`. Don't forget to catch it.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
You can still use callbacks instead.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
XEP plugin integration
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The same changes from the SleekXMPP API apply, so you can do:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    iq_info = yield from self.xmpp['xep_0030'].get_info(jid)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
But the following will only return a Future:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    iq_info = self.xmpp['xep_0030'].get_info(jid)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Callbacks, Event Handlers, and Stream Handlers
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
IQ callbacks and :term:`Event Handlers <event handler>` can be coroutine
 | 
					 | 
				
			||||||
functions; in this case, they will be scheduled in the event loop using
 | 
					 | 
				
			||||||
:meth:`.asyncio.async` and not ran immediately.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
A :class:`.CoroutineCallback` class has been added as well for
 | 
					 | 
				
			||||||
:term:`Stream Handlers <stream handler>`, which will use
 | 
					 | 
				
			||||||
:meth:`.asyncio.async` to schedule the callback.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Running the event loop
 | 
					 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
:meth:`.XMLStream.process` is only a thin wrapper on top of
 | 
					 | 
				
			||||||
``loop.run_forever()`` (if ``timeout`` is provided then it will
 | 
					 | 
				
			||||||
only run for this amount of time, and if ``forever`` is False it will
 | 
					 | 
				
			||||||
run until disconnection).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Therefore you can handle the event loop in any way you like
 | 
					 | 
				
			||||||
instead of using ``process()``.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Examples
 | 
					 | 
				
			||||||
~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Blocking until the session is established
 | 
					 | 
				
			||||||
-----------------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This code blocks until the XMPP session is fully established, which
 | 
					 | 
				
			||||||
can be useful to make sure external events aren’t triggering XMPP
 | 
					 | 
				
			||||||
callbacks while everything is not ready.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    import asyncio, slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    client = slixmpp.ClientXMPP('jid@example', 'password')
 | 
					 | 
				
			||||||
    client.connected_event = asyncio.Event()
 | 
					 | 
				
			||||||
    callback = lambda _: client.connected_event.set()
 | 
					 | 
				
			||||||
    client.add_event_handler('session_start', callback)
 | 
					 | 
				
			||||||
    client.connect()
 | 
					 | 
				
			||||||
    loop.run_until_complete(event.wait())
 | 
					 | 
				
			||||||
    # do some other stuff before running the event loop, e.g.
 | 
					 | 
				
			||||||
    # loop.run_until_complete(httpserver.init())
 | 
					 | 
				
			||||||
    client.process()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Use with other asyncio-based libraries
 | 
					 | 
				
			||||||
--------------------------------------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This code interfaces with aiohttp to retrieve two pages asynchronously
 | 
					 | 
				
			||||||
when the session is established, and then send the HTML content inside
 | 
					 | 
				
			||||||
a simple <message>.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    import asyncio, aiohttp, slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @asyncio.coroutine
 | 
					 | 
				
			||||||
    def get_pythonorg(event):
 | 
					 | 
				
			||||||
        req = yield from aiohttp.request('get', 'http://www.python.org')
 | 
					 | 
				
			||||||
        text = yield from req.text
 | 
					 | 
				
			||||||
        client.send_message(mto='jid2@example', mbody=text)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @asyncio.coroutine
 | 
					 | 
				
			||||||
    def get_asyncioorg(event):
 | 
					 | 
				
			||||||
        req = yield from aiohttp.request('get', 'http://www.asyncio.org')
 | 
					 | 
				
			||||||
        text = yield from req.text
 | 
					 | 
				
			||||||
        client.send_message(mto='jid3@example', mbody=text)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    client = slixmpp.ClientXMPP('jid@example', 'password')
 | 
					 | 
				
			||||||
    client.add_event_handler('session_start', get_pythonorg)
 | 
					 | 
				
			||||||
    client.add_event_handler('session_start', get_asyncioorg)
 | 
					 | 
				
			||||||
    client.connect()
 | 
					 | 
				
			||||||
    client.process()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Blocking Iq
 | 
					 | 
				
			||||||
-----------
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This client checks (via XEP-0092) the software used by every entity it
 | 
					 | 
				
			||||||
receives a message from. After this, it sends a message to a specific
 | 
					 | 
				
			||||||
JID indicating its findings.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    import asyncio, slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class ExampleClient(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
        def __init__(self, *args, **kwargs):
 | 
					 | 
				
			||||||
            slixmpp.ClientXMPP.__init__(self, *args, **kwargs)
 | 
					 | 
				
			||||||
            self.register_plugin('xep_0092')
 | 
					 | 
				
			||||||
            self.add_event_handler('message', self.on_message)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        @asyncio.coroutine
 | 
					 | 
				
			||||||
        def on_message(self, event):
 | 
					 | 
				
			||||||
            # You should probably handle IqError and IqTimeout exceptions here
 | 
					 | 
				
			||||||
            # but this is an example.
 | 
					 | 
				
			||||||
            version = yield from self['xep_0092'].get_version(message['from'])
 | 
					 | 
				
			||||||
            text = "%s sent me a message, he runs %s" % (message['from'],
 | 
					 | 
				
			||||||
                                                         version['software_version']['name'])
 | 
					 | 
				
			||||||
            self.send_message(mto='master@example.tld', mbody=text)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    client = ExampleClient('jid@example', 'password')
 | 
					 | 
				
			||||||
    client.connect()
 | 
					 | 
				
			||||||
    client.process()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@@ -1,50 +0,0 @@
 | 
				
			|||||||
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
 | 
					 | 
				
			||||||
@@ -1,249 +0,0 @@
 | 
				
			|||||||
Following *XMPP: The Definitive Guide*
 | 
					 | 
				
			||||||
======================================
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Slixmpp 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
 | 
					 | 
				
			||||||
Slixmpp release, is available at http://github.com/legastero/xmpp-tdg.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
However, since publication, Slixmpp 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 Slixmpp.
 | 
					 | 
				
			||||||
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.send_message(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 Slixmpp 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 = {slixmpp.stanza.presence.Presence: "status",
 | 
					 | 
				
			||||||
                    slixmpp.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.send_message(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; Slixmpp 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 ``make_presence`` 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.send_message``.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Updated Code
 | 
					 | 
				
			||||||
~~~~~~~~~~~~
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. code-block:: python
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def handleXMPPPresenceProbe(self, event) :
 | 
					 | 
				
			||||||
    self.xmpp.send_presence(pto = event["from"])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def handleXMPPPresenceSubscription(self, subscription) :
 | 
					 | 
				
			||||||
    if subscription["type"] == "subscribe" :
 | 
					 | 
				
			||||||
      userJID = subscription["from"].jid
 | 
					 | 
				
			||||||
      self.xmpp.send_presence_subscription(pto=userJID, ptype="subscribed")
 | 
					 | 
				
			||||||
      self.xmpp.send_presence(pto = userJID)
 | 
					 | 
				
			||||||
      self.xmpp.send_presence_subscription(pto=userJID, ptype="subscribe")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def handleMessageAddedToBackend(self, message) :
 | 
					 | 
				
			||||||
    body = message.user + ": " + message.text
 | 
					 | 
				
			||||||
    for subscriberJID in self.backend.getSubscriberJIDs(message.user) :
 | 
					 | 
				
			||||||
      self.xmpp.send_message(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 Slixmpp 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
 | 
					 | 
				
			||||||
Slixmpp. 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.send_presence_subscription(
 | 
					 | 
				
			||||||
          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>`_
 | 
					 | 
				
			||||||
							
								
								
									
										48
									
								
								example.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								example.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					# coding=utf8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import sleekxmpp
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					from optparse import OptionParser
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if sys.version_info < (3,0):
 | 
				
			||||||
 | 
						reload(sys)
 | 
				
			||||||
 | 
						sys.setdefaultencoding('utf8')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Example(sleekxmpp.ClientXMPP):
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def __init__(self, jid, password):
 | 
				
			||||||
 | 
							sleekxmpp.ClientXMPP.__init__(self, jid, password)
 | 
				
			||||||
 | 
							self.add_event_handler("session_start", self.start)
 | 
				
			||||||
 | 
							self.add_event_handler("message", self.message)
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						def start(self, event):
 | 
				
			||||||
 | 
							self.getRoster()
 | 
				
			||||||
 | 
							self.sendPresence()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def message(self, msg):
 | 
				
			||||||
 | 
							msg.reply("Thanks for sending\n%(body)s" % msg).send()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
						#parse command line arguements
 | 
				
			||||||
 | 
						optp = OptionParser()
 | 
				
			||||||
 | 
						optp.add_option('-q','--quiet', help='set logging to ERROR', action='store_const', dest='loglevel', const=logging.ERROR, default=logging.INFO)
 | 
				
			||||||
 | 
						optp.add_option('-d','--debug', help='set logging to DEBUG', action='store_const', dest='loglevel', const=logging.DEBUG, default=logging.INFO)
 | 
				
			||||||
 | 
						optp.add_option('-v','--verbose', help='set logging to COMM', action='store_const', dest='loglevel', const=5, default=logging.INFO)
 | 
				
			||||||
 | 
						optp.add_option("-c","--config", dest="configfile", default="config.xml", help="set config file to use")
 | 
				
			||||||
 | 
						opts,args = optp.parse_args()
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						logging.basicConfig(level=opts.loglevel, format='%(levelname)-8s %(message)s')
 | 
				
			||||||
 | 
						xmpp = Example('user@gmail.com/sleekxmpp', 'password')
 | 
				
			||||||
 | 
						xmpp.registerPlugin('xep_0030')	
 | 
				
			||||||
 | 
						xmpp.registerPlugin('xep_0004')
 | 
				
			||||||
 | 
						xmpp.registerPlugin('xep_0060')
 | 
				
			||||||
 | 
						xmpp.registerPlugin('xep_0199')
 | 
				
			||||||
 | 
						if xmpp.connect(('talk.google.com', 5222)):
 | 
				
			||||||
 | 
							xmpp.process(threaded=False)
 | 
				
			||||||
 | 
							print("done")
 | 
				
			||||||
 | 
						else:
 | 
				
			||||||
 | 
							print("Unable to connect.")
 | 
				
			||||||
@@ -1,182 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Implementation of xeps for Internet of Things
 | 
					 | 
				
			||||||
    http://wiki.xmpp.org/web/Tech_pages/IoT_systems
 | 
					 | 
				
			||||||
    Copyright (C) 2013 Sustainable Innovation, Joachim.lindborg@sust.se
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from os.path import basename, join as pjoin
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
from urllib import urlopen
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
from slixmpp.plugins.xep_0323.device import Device
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#from slixmpp.exceptions import IqError, IqTimeout
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class IoT_TestDevice(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A simple IoT device that can act as server or client
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    def __init__(self, jid, password):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.session_start)
 | 
					 | 
				
			||||||
        self.add_event_handler("message", self.message)
 | 
					 | 
				
			||||||
        self.device=None
 | 
					 | 
				
			||||||
        self.releaseMe=False
 | 
					 | 
				
			||||||
        self.beServer=True
 | 
					 | 
				
			||||||
        self.clientJID=None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def datacallback(self,from_jid,result,nodeId=None,timestamp=None,fields=None,error_msg=None):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        This method will be called when you ask another IoT device for data with the xep_0323
 | 
					 | 
				
			||||||
        se script below for the registration of the callback
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        logging.debug("we got data %s from %s",str(result),from_jid)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def beClientOrServer(self,server=True,clientJID=None ):
 | 
					 | 
				
			||||||
        if server:
 | 
					 | 
				
			||||||
            self.beServer=True
 | 
					 | 
				
			||||||
            self.clientJID=None
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            self.beServer=False
 | 
					 | 
				
			||||||
            self.clientJID=clientJID
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def testForRelease(self):
 | 
					 | 
				
			||||||
        # todo thread safe
 | 
					 | 
				
			||||||
        return self.releaseMe
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def doReleaseMe(self):
 | 
					 | 
				
			||||||
        # todo thread safe
 | 
					 | 
				
			||||||
        self.releaseMe=True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def addDevice(self, device):
 | 
					 | 
				
			||||||
        self.device=device
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def session_start(self, event):
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
        # tell your preffered friend that you are alive
 | 
					 | 
				
			||||||
        self.send_message(mto='jocke@jabber.sust.se', mbody=self.boundjid.bare +' is now online use xep_323 stanza to talk to me')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if not(self.beServer):
 | 
					 | 
				
			||||||
            session=self['xep_0323'].request_data(self.boundjid.full,self.clientJID,self.datacallback)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def message(self, msg):
 | 
					 | 
				
			||||||
        if msg['type'] in ('chat', 'normal'):
 | 
					 | 
				
			||||||
            logging.debug("got normal chat message" + str(msg))
 | 
					 | 
				
			||||||
            ip=urlopen('http://icanhazip.com').read()
 | 
					 | 
				
			||||||
            msg.reply("Hi I am " + self.boundjid.full + " and I am on IP " + ip).send()
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            logging.debug("got unknown message type %s", str(msg['type']))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TheDevice(Device):
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    This is the actual device object that you will use to get information from your real hardware
 | 
					 | 
				
			||||||
    You will be called in the refresh method when someone is requesting information from you
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    def __init__(self,nodeId):
 | 
					 | 
				
			||||||
        Device.__init__(self,nodeId)
 | 
					 | 
				
			||||||
        self.counter=0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def refresh(self,fields):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        the implementation of the refresh method
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self._set_momentary_timestamp(self._get_timestamp())
 | 
					 | 
				
			||||||
        self.counter+=self.counter
 | 
					 | 
				
			||||||
        self._add_field_momentary_data(self, "Temperature", self.counter)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    #
 | 
					 | 
				
			||||||
    # This script can act both as
 | 
					 | 
				
			||||||
    #   "server" an IoT device that can provide sensorinformation
 | 
					 | 
				
			||||||
    #   python IoT_TestDevice.py -j "serverjid@yourdomain.com" -p "password" -n "TestIoT" --debug
 | 
					 | 
				
			||||||
    #
 | 
					 | 
				
			||||||
    #   "client" an IoT device or other party that would like to get data from another device
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-t", "--pingto", help="set jid to ping",
 | 
					 | 
				
			||||||
                        action="store", type="string", dest="pingjid",
 | 
					 | 
				
			||||||
                        default=None)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # IoT test
 | 
					 | 
				
			||||||
    parser.add_argument("-c", "--sensorjid", dest="sensorjid",
 | 
					 | 
				
			||||||
                        help="Another device to call for data on", default=None)
 | 
					 | 
				
			||||||
    parser.add_argument("-n", "--nodeid", dest="nodeid",
 | 
					 | 
				
			||||||
                        help="I am a device get ready to be called", default=None)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
     # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    xmpp = IoT_TestDevice(args.jid,args.password)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0030')
 | 
					 | 
				
			||||||
    #xmpp['xep_0030'].add_feature(feature='urn:xmpp:iot:sensordata',
 | 
					 | 
				
			||||||
    #                             node=None,
 | 
					 | 
				
			||||||
    #    jid=None)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0323')
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0325')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.nodeid:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # xmpp['xep_0030'].add_feature(feature='urn:xmpp:sn',
 | 
					 | 
				
			||||||
        # node=args.nodeid,
 | 
					 | 
				
			||||||
        # jid=xmpp.boundjid.full)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        myDevice = TheDevice(args.nodeid);
 | 
					 | 
				
			||||||
        # myDevice._add_field(name="Relay", typename="numeric", unit="Bool");
 | 
					 | 
				
			||||||
        myDevice._add_field(name="Temperature", typename="numeric", unit="C")
 | 
					 | 
				
			||||||
        myDevice._set_momentary_timestamp("2013-03-07T16:24:30")
 | 
					 | 
				
			||||||
        myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        xmpp['xep_0323'].register_node(nodeId=args.nodeid, device=myDevice, commTimeout=10);
 | 
					 | 
				
			||||||
        xmpp.beClientOrServer(server=True)
 | 
					 | 
				
			||||||
        while not(xmpp.testForRelease()):
 | 
					 | 
				
			||||||
            xmpp.connect()
 | 
					 | 
				
			||||||
            xmpp.process(block=True)
 | 
					 | 
				
			||||||
            logging.debug("lost connection")
 | 
					 | 
				
			||||||
    if args.sensorjid:
 | 
					 | 
				
			||||||
        logging.debug("will try to call another device for data")
 | 
					 | 
				
			||||||
        xmpp.beClientOrServer(server=False,clientJID=args.sensorjid)
 | 
					 | 
				
			||||||
        xmpp.connect()
 | 
					 | 
				
			||||||
        xmpp.process(block=True)
 | 
					 | 
				
			||||||
        logging.debug("ready ending")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
       print("noopp didn't happen")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@@ -1,173 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2010  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class CommandBot(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A simple Slixmpp bot that provides a basic
 | 
					 | 
				
			||||||
    adhoc command.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The session_start event will be triggered when
 | 
					 | 
				
			||||||
        # the bot establishes its connection with the server
 | 
					 | 
				
			||||||
        # and the XML streams are ready for use. We want to
 | 
					 | 
				
			||||||
        # listen for this event so that we we can initialize
 | 
					 | 
				
			||||||
        # our roster.
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # We add the command after session_start has fired
 | 
					 | 
				
			||||||
        # to ensure that the correct full JID is used.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # If using a component, may also pass jid keyword parameter.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self['xep_0050'].add_command(node='greeting',
 | 
					 | 
				
			||||||
                                     name='Greeting',
 | 
					 | 
				
			||||||
                                     handler=self._handle_command)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _handle_command(self, iq, session):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Respond to the initial request for a command.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            iq      -- The iq stanza containing the command request.
 | 
					 | 
				
			||||||
            session -- A dictionary of data relevant to the command
 | 
					 | 
				
			||||||
                       session. Additional, custom data may be saved
 | 
					 | 
				
			||||||
                       here to persist across handler callbacks.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        form = self['xep_0004'].makeForm('form', 'Greeting')
 | 
					 | 
				
			||||||
        form['instructions'] = 'Send a custom greeting to a JID'
 | 
					 | 
				
			||||||
        form.addField(var='greeting',
 | 
					 | 
				
			||||||
                      ftype='text-single',
 | 
					 | 
				
			||||||
                      label='Your greeting')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        session['payload'] = form
 | 
					 | 
				
			||||||
        session['next'] = self._handle_command_complete
 | 
					 | 
				
			||||||
        session['has_next'] = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Other useful session values:
 | 
					 | 
				
			||||||
        # session['to']                    -- The JID that received the
 | 
					 | 
				
			||||||
        #                                     command request.
 | 
					 | 
				
			||||||
        # session['from']                  -- The JID that sent the
 | 
					 | 
				
			||||||
        #                                     command request.
 | 
					 | 
				
			||||||
        # session['has_next'] = True       -- There are more steps to complete
 | 
					 | 
				
			||||||
        # session['allow_complete'] = True -- Allow user to finish immediately
 | 
					 | 
				
			||||||
        #                                     and possibly skip steps
 | 
					 | 
				
			||||||
        # session['cancel'] = handler      -- Assign a handler for if the user
 | 
					 | 
				
			||||||
        #                                     cancels the command.
 | 
					 | 
				
			||||||
        # session['notes'] = [             -- Add informative notes about the
 | 
					 | 
				
			||||||
        #   ('info', 'Info message'),         command's results.
 | 
					 | 
				
			||||||
        #   ('warning', 'Warning message'),
 | 
					 | 
				
			||||||
        #   ('error', 'Error message')]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return session
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _handle_command_complete(self, payload, session):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process a command result from the user.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            payload -- Either a single item, such as a form, or a list
 | 
					 | 
				
			||||||
                       of items or forms if more than one form was
 | 
					 | 
				
			||||||
                       provided to the user. The payload may be any
 | 
					 | 
				
			||||||
                       stanza, such as jabber:x:oob for out of band
 | 
					 | 
				
			||||||
                       data, or jabber:x:data for typical data forms.
 | 
					 | 
				
			||||||
            session -- A dictionary of data relevant to the command
 | 
					 | 
				
			||||||
                       session. Additional, custom data may be saved
 | 
					 | 
				
			||||||
                       here to persist across handler callbacks.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # In this case (as is typical), the payload is a form
 | 
					 | 
				
			||||||
        form = payload
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        greeting = form['values']['greeting']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.send_message(mto=session['from'],
 | 
					 | 
				
			||||||
                          mbody="%s, World!" % greeting,
 | 
					 | 
				
			||||||
                          mtype='chat')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Having no return statement is the same as unsetting the 'payload'
 | 
					 | 
				
			||||||
        # and 'next' session values and returning the session.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Unless it is the final step, always return the session dictionary.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        session['payload'] = None
 | 
					 | 
				
			||||||
        session['next'] = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return session
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the CommandBot and register plugins. Note that while plugins may
 | 
					 | 
				
			||||||
    # have interdependencies, the order in which you register them does
 | 
					 | 
				
			||||||
    # not matter.
 | 
					 | 
				
			||||||
    xmpp = CommandBot(args.jid, args.password)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0030') # Service Discovery
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0004') # Data Forms
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0050') # Adhoc Commands
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0199', {'keepalive': True, 'frequency':15})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
@@ -1,179 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2010  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class CommandUserBot(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A simple Slixmpp bot that uses the adhoc command
 | 
					 | 
				
			||||||
    provided by the adhoc_provider.py example.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password, other, greeting):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.command_provider = other
 | 
					 | 
				
			||||||
        self.greeting = greeting
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The session_start event will be triggered when
 | 
					 | 
				
			||||||
        # the bot establishes its connection with the server
 | 
					 | 
				
			||||||
        # and the XML streams are ready for use. We want to
 | 
					 | 
				
			||||||
        # listen for this event so that we we can initialize
 | 
					 | 
				
			||||||
        # our roster.
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
        self.add_event_handler("message", self.message)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # We first create a session dictionary containing:
 | 
					 | 
				
			||||||
        #   'next'  -- the handler to execute on a successful response
 | 
					 | 
				
			||||||
        #   'error' -- the handler to execute if an error occurs
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The session may also contain custom data.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        session = {'greeting': self.greeting,
 | 
					 | 
				
			||||||
                   'next': self._command_start,
 | 
					 | 
				
			||||||
                   'error': self._command_error}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self['xep_0050'].start_command(jid=self.command_provider,
 | 
					 | 
				
			||||||
                                       node='greeting',
 | 
					 | 
				
			||||||
                                       session=session)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def message(self, msg):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process incoming message stanzas.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            msg -- The received message stanza.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        logging.info(msg['body'])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _command_start(self, iq, session):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the initial command result.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            iq      -- The iq stanza containing the command result.
 | 
					 | 
				
			||||||
            session -- A dictionary of data relevant to the command
 | 
					 | 
				
			||||||
                       session. Additional, custom data may be saved
 | 
					 | 
				
			||||||
                       here to persist across handler callbacks.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The greeting command provides a form with a single field:
 | 
					 | 
				
			||||||
        # <x xmlns="jabber:x:data" type="form">
 | 
					 | 
				
			||||||
        #   <field var="greeting"
 | 
					 | 
				
			||||||
        #          type="text-single"
 | 
					 | 
				
			||||||
        #          label="Your greeting" />
 | 
					 | 
				
			||||||
        # </x>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        form = self['xep_0004'].makeForm(ftype='submit')
 | 
					 | 
				
			||||||
        form.addField(var='greeting',
 | 
					 | 
				
			||||||
                      value=session['greeting'])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        session['payload'] = form
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # We don't need to process the next result.
 | 
					 | 
				
			||||||
        session['next'] = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Other options include using:
 | 
					 | 
				
			||||||
        # continue_command() -- Continue to the next step in the workflow
 | 
					 | 
				
			||||||
        # cancel_command()   -- Stop command execution.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self['xep_0050'].complete_command(session)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _command_error(self, iq, session):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process an error that occurs during command execution.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            iq      -- The iq stanza containing the error.
 | 
					 | 
				
			||||||
            session -- A dictionary of data relevant to the command
 | 
					 | 
				
			||||||
                       session. Additional, custom data may be saved
 | 
					 | 
				
			||||||
                       here to persist across handler callbacks.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        logging.error("COMMAND: %s %s" % (iq['error']['condition'],
 | 
					 | 
				
			||||||
                                          iq['error']['text']))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Terminate the command's execution and clear its session.
 | 
					 | 
				
			||||||
        # The session will automatically be cleared if no error
 | 
					 | 
				
			||||||
        # handler is provided.
 | 
					 | 
				
			||||||
        self['xep_0050'].terminate_command(session)
 | 
					 | 
				
			||||||
        self.disconnect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-o", "--other", dest="other",
 | 
					 | 
				
			||||||
                        help="JID providing commands")
 | 
					 | 
				
			||||||
    parser.add_argument("-g", "--greeting", dest="greeting",
 | 
					 | 
				
			||||||
                        help="Greeting")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
    if args.other is None:
 | 
					 | 
				
			||||||
        args.other = input("JID Providing Commands: ")
 | 
					 | 
				
			||||||
    if args.greeting is None:
 | 
					 | 
				
			||||||
        args.greeting = input("Greeting: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the CommandBot and register plugins. Note that while plugins may
 | 
					 | 
				
			||||||
    # have interdependencies, the order in which you register them does
 | 
					 | 
				
			||||||
    # not matter.
 | 
					 | 
				
			||||||
    xmpp = CommandUserBot(args.jid, args.password, args.other, args.greeting)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0030') # Service Discovery
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0004') # Data Forms
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0050') # Adhoc Commands
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
@@ -1,147 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2010  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class AdminCommands(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A simple Slixmpp bot that uses admin commands to
 | 
					 | 
				
			||||||
    add a new user to a server.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password, command):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.command = command
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def command_success(iq, session):
 | 
					 | 
				
			||||||
            print('Command completed')
 | 
					 | 
				
			||||||
            if iq['command']['form']:
 | 
					 | 
				
			||||||
                for var, field in iq['command']['form']['fields'].items():
 | 
					 | 
				
			||||||
                    print('%s: %s' % (var, field['value']))
 | 
					 | 
				
			||||||
            if iq['command']['notes']:
 | 
					 | 
				
			||||||
                print('Command Notes:')
 | 
					 | 
				
			||||||
                for note in iq['command']['notes']:
 | 
					 | 
				
			||||||
                    print('%s: %s' % note)
 | 
					 | 
				
			||||||
            self.disconnect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def command_error(iq, session):
 | 
					 | 
				
			||||||
            print('Error completing command')
 | 
					 | 
				
			||||||
            print('%s: %s' % (iq['error']['condition'],
 | 
					 | 
				
			||||||
                              iq['error']['text']))
 | 
					 | 
				
			||||||
            self['xep_0050'].terminate_command(session)
 | 
					 | 
				
			||||||
            self.disconnect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def process_form(iq, session):
 | 
					 | 
				
			||||||
            form = iq['command']['form']
 | 
					 | 
				
			||||||
            answers = {}
 | 
					 | 
				
			||||||
            for var, field in form['fields'].items():
 | 
					 | 
				
			||||||
                if var != 'FORM_TYPE':
 | 
					 | 
				
			||||||
                    if field['type'] == 'boolean':
 | 
					 | 
				
			||||||
                        answers[var] = input('%s (y/n): ' % field['label'])
 | 
					 | 
				
			||||||
                        if answers[var].lower() in ('1', 'true', 'y', 'yes'):
 | 
					 | 
				
			||||||
                            answers[var] = '1'
 | 
					 | 
				
			||||||
                        else:
 | 
					 | 
				
			||||||
                            answers[var] = '0'
 | 
					 | 
				
			||||||
                    else:
 | 
					 | 
				
			||||||
                        answers[var] = input('%s: ' % field['label'])
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    answers['FORM_TYPE'] = field['value']
 | 
					 | 
				
			||||||
            form['type'] = 'submit'
 | 
					 | 
				
			||||||
            form['values'] = answers
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            session['next'] = command_success
 | 
					 | 
				
			||||||
            session['payload'] = form
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            self['xep_0050'].complete_command(session)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        session = {'next': process_form,
 | 
					 | 
				
			||||||
                   'error': command_error}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        command = self.command.replace('-', '_')
 | 
					 | 
				
			||||||
        handler = getattr(self['xep_0133'], command, None)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if handler:
 | 
					 | 
				
			||||||
            handler(session={
 | 
					 | 
				
			||||||
                'next': process_form,
 | 
					 | 
				
			||||||
                'error': command_error
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            print('Invalid command name: %s' % self.command)
 | 
					 | 
				
			||||||
            self.disconnect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-c", "--command", dest="command",
 | 
					 | 
				
			||||||
                        help="admin command to use")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
    if args.command is None:
 | 
					 | 
				
			||||||
        args.command = input("Admin command: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the CommandBot and register plugins. Note that while plugins may
 | 
					 | 
				
			||||||
    # have interdependencies, the order in which you register them does
 | 
					 | 
				
			||||||
    # not matter.
 | 
					 | 
				
			||||||
    xmpp = AdminCommands(args.jid, args.password, args.command)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0133') # Service Administration
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
@@ -1,137 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2010  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from slixmpp import ClientXMPP, Iq
 | 
					 | 
				
			||||||
from slixmpp.exceptions import IqError, IqTimeout, XMPPError
 | 
					 | 
				
			||||||
from slixmpp.xmlstream import register_stanza_plugin
 | 
					 | 
				
			||||||
from slixmpp.xmlstream.handler import Callback
 | 
					 | 
				
			||||||
from slixmpp.xmlstream.matcher import StanzaPath
 | 
					 | 
				
			||||||
from stanza import Action
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ActionBot(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A simple Slixmpp bot that receives a custom stanza
 | 
					 | 
				
			||||||
    from another client.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The session_start event will be triggered when
 | 
					 | 
				
			||||||
        # the bot establishes its connection with the server
 | 
					 | 
				
			||||||
        # and the XML streams are ready for use. We want to
 | 
					 | 
				
			||||||
        # listen for this event so that we we can initialize
 | 
					 | 
				
			||||||
        # our roster.
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.register_handler(
 | 
					 | 
				
			||||||
          Callback('Some custom iq',
 | 
					 | 
				
			||||||
            StanzaPath('iq@type=set/action'),
 | 
					 | 
				
			||||||
            self._handle_action))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.add_event_handler('custom_action',
 | 
					 | 
				
			||||||
                self._handle_action_event)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        register_stanza_plugin(Iq, Action)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _handle_action(self, iq):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Raise an event for the stanza so that it can be processed in its
 | 
					 | 
				
			||||||
        own thread without blocking the main stanza processing loop.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.event('custom_action', iq)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _handle_action_event(self, iq):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Respond to the custom action event.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        method = iq['action']['method']
 | 
					 | 
				
			||||||
        param = iq['action']['param']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if method == 'is_prime' and param == '2':
 | 
					 | 
				
			||||||
            print("got message: %s" % iq)
 | 
					 | 
				
			||||||
            iq.reply()
 | 
					 | 
				
			||||||
            iq['action']['status'] = 'done'
 | 
					 | 
				
			||||||
            iq.send()
 | 
					 | 
				
			||||||
        elif method == 'bye':
 | 
					 | 
				
			||||||
            print("got message: %s" % iq)
 | 
					 | 
				
			||||||
            self.disconnect()
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            print("got message: %s" % iq)
 | 
					 | 
				
			||||||
            iq.reply()
 | 
					 | 
				
			||||||
            iq['action']['status'] = 'error'
 | 
					 | 
				
			||||||
            iq.send()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the CommandBot and register plugins. Note that while plugins may
 | 
					 | 
				
			||||||
    # have interdependencies, the order in which you register them does
 | 
					 | 
				
			||||||
    # not matter.
 | 
					 | 
				
			||||||
    xmpp = ActionBot(args.jid, args.password)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0030') # Service Discovery
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0004') # Data Forms
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0050') # Adhoc Commands
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0199', {'keepalive': True, 'frequency':15})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
@@ -1,142 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2010  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
from slixmpp import Iq
 | 
					 | 
				
			||||||
from slixmpp.exceptions import XMPPError
 | 
					 | 
				
			||||||
from slixmpp.xmlstream import register_stanza_plugin
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from stanza import Action
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ActionUserBot(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A simple Slixmpp bot that sends a custom action stanza
 | 
					 | 
				
			||||||
    to another client.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password, other):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.action_provider = other
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The session_start event will be triggered when
 | 
					 | 
				
			||||||
        # the bot establishes its connection with the server
 | 
					 | 
				
			||||||
        # and the XML streams are ready for use. We want to
 | 
					 | 
				
			||||||
        # listen for this event so that we we can initialize
 | 
					 | 
				
			||||||
        # our roster.
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
        self.add_event_handler("message", self.message)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        register_stanza_plugin(Iq, Action)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.send_custom_iq()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def send_custom_iq(self):
 | 
					 | 
				
			||||||
        """Create and send two custom actions.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        If the first action was successful, then send
 | 
					 | 
				
			||||||
        a shutdown command and then disconnect.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        iq = self.Iq()
 | 
					 | 
				
			||||||
        iq['to'] = self.action_provider
 | 
					 | 
				
			||||||
        iq['type'] = 'set'
 | 
					 | 
				
			||||||
        iq['action']['method'] = 'is_prime'
 | 
					 | 
				
			||||||
        iq['action']['param'] = '2'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            resp = iq.send()
 | 
					 | 
				
			||||||
            if resp['action']['status'] == 'done':
 | 
					 | 
				
			||||||
                #sending bye
 | 
					 | 
				
			||||||
                iq2 = self.Iq()
 | 
					 | 
				
			||||||
                iq2['to'] = self.action_provider
 | 
					 | 
				
			||||||
                iq2['type'] = 'set'
 | 
					 | 
				
			||||||
                iq2['action']['method'] = 'bye'
 | 
					 | 
				
			||||||
                iq2.send(block=False)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                self.disconnect()
 | 
					 | 
				
			||||||
        except XMPPError:
 | 
					 | 
				
			||||||
            print('There was an error sending the custom action.')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def message(self, msg):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process incoming message stanzas.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            msg -- The received message stanza.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        logging.info(msg['body'])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-o", "--other", dest="other",
 | 
					 | 
				
			||||||
                        help="JID providing custom stanza")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
    if args.other is None:
 | 
					 | 
				
			||||||
        args.other = input("JID Providing custom stanza: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the CommandBot and register plugins. Note that while plugins may
 | 
					 | 
				
			||||||
    # have interdependencies, the order in which you register them does
 | 
					 | 
				
			||||||
    # not matter.
 | 
					 | 
				
			||||||
    xmpp = ActionUserBot(args.jid, args.password, args.other)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0030') # Service Discovery
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0004') # Data Forms
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0050') # Adhoc Commands
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
@@ -1,56 +0,0 @@
 | 
				
			|||||||
from slixmpp.xmlstream import ElementBase
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Action(ElementBase):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A stanza class for XML content of the form:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <action xmlns="slixmpp:custom:actions">
 | 
					 | 
				
			||||||
      <method>X</method>
 | 
					 | 
				
			||||||
      <param>X</param>
 | 
					 | 
				
			||||||
      <status>X</status>
 | 
					 | 
				
			||||||
    </action>
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #: The `name` field refers to the basic XML tag name of the
 | 
					 | 
				
			||||||
    #: stanza. Here, the tag name will be 'action'.
 | 
					 | 
				
			||||||
    name = 'action'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #: The namespace of the main XML tag.
 | 
					 | 
				
			||||||
    namespace = 'slixmpp:custom:actions'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #: The `plugin_attrib` value is the name that can be used
 | 
					 | 
				
			||||||
    #: with a parent stanza to access this stanza. For example
 | 
					 | 
				
			||||||
    #: from an Iq stanza object, accessing:
 | 
					 | 
				
			||||||
    #:
 | 
					 | 
				
			||||||
    #:     iq['action']
 | 
					 | 
				
			||||||
    #:
 | 
					 | 
				
			||||||
    #: would reference an Action object, and will even create
 | 
					 | 
				
			||||||
    #: an Action object and append it to the Iq stanza if
 | 
					 | 
				
			||||||
    #: one doesn't already exist.
 | 
					 | 
				
			||||||
    plugin_attrib = 'action'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #: Stanza objects expose dictionary-like interfaces for
 | 
					 | 
				
			||||||
    #: accessing and manipulating substanzas and other values.
 | 
					 | 
				
			||||||
    #: The set of interfaces defined here are the names of
 | 
					 | 
				
			||||||
    #: these dictionary-like interfaces provided by this stanza
 | 
					 | 
				
			||||||
    #: type. For example, an Action stanza object can use:
 | 
					 | 
				
			||||||
    #:
 | 
					 | 
				
			||||||
    #:     action['method'] = 'foo'
 | 
					 | 
				
			||||||
    #:     print(action['param'])
 | 
					 | 
				
			||||||
    #:     del action['status']
 | 
					 | 
				
			||||||
    #:
 | 
					 | 
				
			||||||
    #: to set, get, or remove its values.
 | 
					 | 
				
			||||||
    interfaces = set(('method', 'param', 'status'))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #: By default, values in the `interfaces` set are mapped to
 | 
					 | 
				
			||||||
    #: attribute values. This can be changed such that an interface
 | 
					 | 
				
			||||||
    #: maps to a subelement's text value by adding interfaces to
 | 
					 | 
				
			||||||
    #: the sub_interfaces set. For example, here all interfaces
 | 
					 | 
				
			||||||
    #: are marked as sub_interfaces, and so the XML produced will
 | 
					 | 
				
			||||||
    #: look like:
 | 
					 | 
				
			||||||
    #:
 | 
					 | 
				
			||||||
    #:     <action xmlns="slixmpp:custom:actions">
 | 
					 | 
				
			||||||
    #:       <method>foo</method>
 | 
					 | 
				
			||||||
    #:     </action>
 | 
					 | 
				
			||||||
    sub_interfaces = interfaces
 | 
					 | 
				
			||||||
@@ -1,161 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2010  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
from slixmpp.exceptions import IqError, IqTimeout
 | 
					 | 
				
			||||||
from slixmpp.xmlstream.asyncio import asyncio
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Disco(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A demonstration for using basic service discovery.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Send a disco#info and disco#items request to a JID/node combination,
 | 
					 | 
				
			||||||
    and print out the results.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    May also request only particular info categories such as just features,
 | 
					 | 
				
			||||||
    or just items.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password, target_jid, target_node='', get=''):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Using service discovery requires the XEP-0030 plugin.
 | 
					 | 
				
			||||||
        self.register_plugin('xep_0030')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.get = get
 | 
					 | 
				
			||||||
        self.target_jid = target_jid
 | 
					 | 
				
			||||||
        self.target_node = target_node
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Values to control which disco entities are reported
 | 
					 | 
				
			||||||
        self.info_types = ['', 'all', 'info', 'identities', 'features']
 | 
					 | 
				
			||||||
        self.identity_types = ['', 'all', 'info', 'identities']
 | 
					 | 
				
			||||||
        self.feature_types = ['', 'all', 'info', 'features']
 | 
					 | 
				
			||||||
        self.items_types = ['', 'all', 'items']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The session_start event will be triggered when
 | 
					 | 
				
			||||||
        # the bot establishes its connection with the server
 | 
					 | 
				
			||||||
        # and the XML streams are ready for use. We want to
 | 
					 | 
				
			||||||
        # listen for this event so that we we can initialize
 | 
					 | 
				
			||||||
        # our roster.
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @asyncio.coroutine
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        In this case, we send disco#info and disco#items
 | 
					 | 
				
			||||||
        stanzas to the requested JID and print the results.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            if self.get in self.info_types:
 | 
					 | 
				
			||||||
                # function using the callback parameter.
 | 
					 | 
				
			||||||
                info = yield from self['xep_0030'].get_info(jid=self.target_jid,
 | 
					 | 
				
			||||||
                                                            node=self.target_node)
 | 
					 | 
				
			||||||
            if self.get in self.items_types:
 | 
					 | 
				
			||||||
                # The same applies from above. Listen for the
 | 
					 | 
				
			||||||
                # disco_items event or pass a callback function
 | 
					 | 
				
			||||||
                # if you need to process a non-blocking request.
 | 
					 | 
				
			||||||
                items = yield from self['xep_0030'].get_items(jid=self.target_jid,
 | 
					 | 
				
			||||||
                                                              node=self.target_node)
 | 
					 | 
				
			||||||
            if self.get not in self.info_types and self.get not in self.items_types:
 | 
					 | 
				
			||||||
                logging.error("Invalid disco request type.")
 | 
					 | 
				
			||||||
                return
 | 
					 | 
				
			||||||
        except IqError as e:
 | 
					 | 
				
			||||||
            logging.error("Entity returned an error: %s" % e.iq['error']['condition'])
 | 
					 | 
				
			||||||
        except IqTimeout:
 | 
					 | 
				
			||||||
            logging.error("No response received.")
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            header = 'XMPP Service Discovery: %s' % self.target_jid
 | 
					 | 
				
			||||||
            print(header)
 | 
					 | 
				
			||||||
            print('-' * len(header))
 | 
					 | 
				
			||||||
            if self.target_node != '':
 | 
					 | 
				
			||||||
                print('Node: %s' % self.target_node)
 | 
					 | 
				
			||||||
                print('-' * len(header))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if self.get in self.identity_types:
 | 
					 | 
				
			||||||
                print('Identities:')
 | 
					 | 
				
			||||||
                for identity in info['disco_info']['identities']:
 | 
					 | 
				
			||||||
                    print('  - %s' % str(identity))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if self.get in self.feature_types:
 | 
					 | 
				
			||||||
                print('Features:')
 | 
					 | 
				
			||||||
                for feature in info['disco_info']['features']:
 | 
					 | 
				
			||||||
                    print('  - %s' % feature)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if self.get in self.items_types:
 | 
					 | 
				
			||||||
                print('Items:')
 | 
					 | 
				
			||||||
                for item in items['disco_items']['items']:
 | 
					 | 
				
			||||||
                    print('  - %s' % str(item))
 | 
					 | 
				
			||||||
        finally:
 | 
					 | 
				
			||||||
            self.disconnect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser(description=Disco.__doc__)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    parser.add_argument("-q","--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const",
 | 
					 | 
				
			||||||
                        dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR,
 | 
					 | 
				
			||||||
                        default=logging.ERROR)
 | 
					 | 
				
			||||||
    parser.add_argument("-d","--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const",
 | 
					 | 
				
			||||||
                        dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG,
 | 
					 | 
				
			||||||
                        default=logging.ERROR)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
    parser.add_argument("query", choices=["all", "info", "items", "identities", "features"])
 | 
					 | 
				
			||||||
    parser.add_argument("target_jid")
 | 
					 | 
				
			||||||
    parser.add_argument("node", nargs='?')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the Disco browser.
 | 
					 | 
				
			||||||
    xmpp = Disco(args.jid, args.password, args.target_jid, args.node, args.query)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process(forever=False)
 | 
					 | 
				
			||||||
@@ -1,165 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2012  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
from slixmpp.exceptions import XMPPError
 | 
					 | 
				
			||||||
from slixmpp import asyncio
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
FILE_TYPES = {
 | 
					 | 
				
			||||||
    'image/png': 'png',
 | 
					 | 
				
			||||||
    'image/gif': 'gif',
 | 
					 | 
				
			||||||
    'image/jpeg': 'jpg'
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class AvatarDownloader(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A basic script for downloading the avatars for a user's contacts.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
        self.add_event_handler("changed_status", self.wait_for_presences)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.add_event_handler('vcard_avatar_update', self.on_vcard_avatar)
 | 
					 | 
				
			||||||
        self.add_event_handler('avatar_metadata_publish', self.on_avatar)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.received = set()
 | 
					 | 
				
			||||||
        self.presences_received = asyncio.Event()
 | 
					 | 
				
			||||||
        self.roster_received = asyncio.Event()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def roster_received_cb(self, event):
 | 
					 | 
				
			||||||
        self.roster_received.set()
 | 
					 | 
				
			||||||
        self.presences_received.clear()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @asyncio.coroutine
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.get_roster(callback=self.roster_received_cb)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        print('Waiting for presence updates...\n')
 | 
					 | 
				
			||||||
        yield from self.roster_received.wait()
 | 
					 | 
				
			||||||
        print('Roster received')
 | 
					 | 
				
			||||||
        yield from self.presences_received.wait()
 | 
					 | 
				
			||||||
        self.disconnect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @asyncio.coroutine
 | 
					 | 
				
			||||||
    def on_vcard_avatar(self, pres):
 | 
					 | 
				
			||||||
        print("Received vCard avatar update from %s" % pres['from'].bare)
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            result = yield from self['xep_0054'].get_vcard(pres['from'].bare, cached=True,
 | 
					 | 
				
			||||||
                                                           timeout=5)
 | 
					 | 
				
			||||||
        except XMPPError:
 | 
					 | 
				
			||||||
            print("Error retrieving avatar for %s" % pres['from'])
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
        avatar = result['vcard_temp']['PHOTO']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        filetype = FILE_TYPES.get(avatar['TYPE'], 'png')
 | 
					 | 
				
			||||||
        filename = 'vcard_avatar_%s_%s.%s' % (
 | 
					 | 
				
			||||||
                pres['from'].bare,
 | 
					 | 
				
			||||||
                pres['vcard_temp_update']['photo'],
 | 
					 | 
				
			||||||
                filetype)
 | 
					 | 
				
			||||||
        with open(filename, 'wb+') as img:
 | 
					 | 
				
			||||||
            img.write(avatar['BINVAL'])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @asyncio.coroutine
 | 
					 | 
				
			||||||
    def on_avatar(self, msg):
 | 
					 | 
				
			||||||
        print("Received avatar update from %s" % msg['from'])
 | 
					 | 
				
			||||||
        metadata = msg['pubsub_event']['items']['item']['avatar_metadata']
 | 
					 | 
				
			||||||
        for info in metadata['items']:
 | 
					 | 
				
			||||||
            if not info['url']:
 | 
					 | 
				
			||||||
                try:
 | 
					 | 
				
			||||||
                    result = yield from self['xep_0084'].retrieve_avatar(msg['from'].bare, info['id'],
 | 
					 | 
				
			||||||
                                                                         timeout=5)
 | 
					 | 
				
			||||||
                except XMPPError:
 | 
					 | 
				
			||||||
                    print("Error retrieving avatar for %s" % msg['from'])
 | 
					 | 
				
			||||||
                    return
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                avatar = result['pubsub']['items']['item']['avatar_data']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                filetype = FILE_TYPES.get(metadata['type'], 'png')
 | 
					 | 
				
			||||||
                filename = 'avatar_%s_%s.%s' % (msg['from'].bare, info['id'], filetype)
 | 
					 | 
				
			||||||
                with open(filename, 'wb+') as img:
 | 
					 | 
				
			||||||
                    img.write(avatar['value'])
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                # We could retrieve the avatar via HTTP, etc here instead.
 | 
					 | 
				
			||||||
                pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def wait_for_presences(self, pres):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Wait to receive updates from all roster contacts.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.received.add(pres['from'].bare)
 | 
					 | 
				
			||||||
        print((len(self.received), len(self.client_roster.keys())))
 | 
					 | 
				
			||||||
        if len(self.received) >= len(self.client_roster.keys()):
 | 
					 | 
				
			||||||
            self.presences_received.set()
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            self.presences_received.clear()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
    parser.add_argument("-q","--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const",
 | 
					 | 
				
			||||||
                        dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR,
 | 
					 | 
				
			||||||
                        default=logging.ERROR)
 | 
					 | 
				
			||||||
    parser.add_argument("-d","--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const",
 | 
					 | 
				
			||||||
                        dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG,
 | 
					 | 
				
			||||||
                        default=logging.ERROR)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    xmpp = AvatarDownloader(args.jid, args.password)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0054')
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0153')
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0084')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
@@ -1,113 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2010  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class EchoBot(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A simple Slixmpp bot that will echo messages it
 | 
					 | 
				
			||||||
    receives, along with a short thank you message.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The session_start event will be triggered when
 | 
					 | 
				
			||||||
        # the bot establishes its connection with the server
 | 
					 | 
				
			||||||
        # and the XML streams are ready for use. We want to
 | 
					 | 
				
			||||||
        # listen for this event so that we we can initialize
 | 
					 | 
				
			||||||
        # our roster.
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The message event is triggered whenever a message
 | 
					 | 
				
			||||||
        # stanza is received. Be aware that that includes
 | 
					 | 
				
			||||||
        # MUC messages and error messages.
 | 
					 | 
				
			||||||
        self.add_event_handler("message", self.message)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def message(self, msg):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process incoming message stanzas. Be aware that this also
 | 
					 | 
				
			||||||
        includes MUC messages and error messages. It is usually
 | 
					 | 
				
			||||||
        a good idea to check the messages's type before processing
 | 
					 | 
				
			||||||
        or sending replies.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            msg -- The received message stanza. See the documentation
 | 
					 | 
				
			||||||
                   for stanza objects and the Message stanza to see
 | 
					 | 
				
			||||||
                   how it may be used.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if msg['type'] in ('chat', 'normal'):
 | 
					 | 
				
			||||||
            msg.reply("Thanks for sending\n%(body)s" % msg).send()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser(description=EchoBot.__doc__)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the EchoBot and register plugins. Note that while plugins may
 | 
					 | 
				
			||||||
    # have interdependencies, the order in which you register them does
 | 
					 | 
				
			||||||
    # not matter.
 | 
					 | 
				
			||||||
    xmpp = EchoBot(args.jid, args.password)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0030') # Service Discovery
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0004') # Data Forms
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0060') # PubSub
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0199') # XMPP Ping
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
@@ -1,105 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2010  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
from slixmpp.componentxmpp import ComponentXMPP
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class EchoComponent(ComponentXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A simple Slixmpp component that echoes messages.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, secret, server, port):
 | 
					 | 
				
			||||||
        ComponentXMPP.__init__(self, jid, secret, server, port)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # You don't need a session_start handler, but that is
 | 
					 | 
				
			||||||
        # where you would broadcast initial presence.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The message event is triggered whenever a message
 | 
					 | 
				
			||||||
        # stanza is received. Be aware that that includes
 | 
					 | 
				
			||||||
        # MUC messages and error messages.
 | 
					 | 
				
			||||||
        self.add_event_handler("message", self.message)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def message(self, msg):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process incoming message stanzas. Be aware that this also
 | 
					 | 
				
			||||||
        includes MUC messages and error messages. It is usually
 | 
					 | 
				
			||||||
        a good idea to check the messages's type before processing
 | 
					 | 
				
			||||||
        or sending replies.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Since a component may send messages from any number of JIDs,
 | 
					 | 
				
			||||||
        it is best to always include a from JID.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            msg -- The received message stanza. See the documentation
 | 
					 | 
				
			||||||
                   for stanza objects and the Message stanza to see
 | 
					 | 
				
			||||||
                   how it may be used.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        # The reply method will use the messages 'to' JID as the
 | 
					 | 
				
			||||||
        # outgoing reply's 'from' JID.
 | 
					 | 
				
			||||||
        msg.reply("Thanks for sending\n%(body)s" % msg).send()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser(description=EchoComponent.__doc__)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-s", "--server", dest="server",
 | 
					 | 
				
			||||||
                        help="server to connect to")
 | 
					 | 
				
			||||||
    parser.add_argument("-P", "--port", dest="port",
 | 
					 | 
				
			||||||
                        help="port to connect to")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Component JID: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
    if args.server is None:
 | 
					 | 
				
			||||||
        args.server = input("Server: ")
 | 
					 | 
				
			||||||
    if args.port is None:
 | 
					 | 
				
			||||||
        args.port = int(input("Port: "))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the EchoComponent and register plugins. Note that while plugins
 | 
					 | 
				
			||||||
    # may have interdependencies, the order in which you register them does
 | 
					 | 
				
			||||||
    # not matter.
 | 
					 | 
				
			||||||
    xmpp = EchoComponent(args.jid, args.password, args.server, args.port)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0030') # Service Discovery
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0004') # Data Forms
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0060') # PubSub
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0199') # XMPP Ping
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
@@ -1,133 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2010  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import ssl
 | 
					 | 
				
			||||||
from slixmpp.xmlstream import cert
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class GTalkBot(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A demonstration of using Slixmpp with accounts from a Google Apps
 | 
					 | 
				
			||||||
    account with a custom domain, because it requires custom certificate
 | 
					 | 
				
			||||||
    validation.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The session_start event will be triggered when
 | 
					 | 
				
			||||||
        # the bot establishes its connection with the server
 | 
					 | 
				
			||||||
        # and the XML streams are ready for use. We want to
 | 
					 | 
				
			||||||
        # listen for this event so that we we can initialize
 | 
					 | 
				
			||||||
        # our roster.
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The message event is triggered whenever a message
 | 
					 | 
				
			||||||
        # stanza is received. Be aware that that includes
 | 
					 | 
				
			||||||
        # MUC messages and error messages.
 | 
					 | 
				
			||||||
        self.add_event_handler("message", self.message)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Using a Google Apps custom domain, the certificate
 | 
					 | 
				
			||||||
        # does not contain the custom domain, just the GTalk
 | 
					 | 
				
			||||||
        # server name. So we will need to process invalid
 | 
					 | 
				
			||||||
        # certifcates ourselves and check that it really
 | 
					 | 
				
			||||||
        # is from Google.
 | 
					 | 
				
			||||||
        self.add_event_handler("ssl_invalid_cert", self.invalid_cert)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def invalid_cert(self, pem_cert):
 | 
					 | 
				
			||||||
        der_cert = ssl.PEM_cert_to_DER_cert(pem_cert)
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            cert.verify('talk.google.com', der_cert)
 | 
					 | 
				
			||||||
            logging.debug("CERT: Found GTalk certificate")
 | 
					 | 
				
			||||||
        except cert.CertificateError as err:
 | 
					 | 
				
			||||||
            log.error(err.message)
 | 
					 | 
				
			||||||
            self.disconnect(send_close=False)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def message(self, msg):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process incoming message stanzas. Be aware that this also
 | 
					 | 
				
			||||||
        includes MUC messages and error messages. It is usually
 | 
					 | 
				
			||||||
        a good idea to check the messages's type before processing
 | 
					 | 
				
			||||||
        or sending replies.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            msg -- The received message stanza. See the documentation
 | 
					 | 
				
			||||||
                   for stanza objects and the Message stanza to see
 | 
					 | 
				
			||||||
                   how it may be used.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if msg['type'] in ('chat', 'normal'):
 | 
					 | 
				
			||||||
            msg.reply("Thanks for sending\n%(body)s" % msg).send()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the GTalkBot and register plugins. Note that while plugins may
 | 
					 | 
				
			||||||
    # have interdependencies, the order in which you register them does
 | 
					 | 
				
			||||||
    # not matter.
 | 
					 | 
				
			||||||
    xmpp = GTalkBot(args.jid, args.password)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0030') # Service Discovery
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0004') # Data Forms
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0060') # PubSub
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0199') # XMPP Ping
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
@@ -1,97 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Implementation of HTTP over XMPP transport
 | 
					 | 
				
			||||||
    http://xmpp.org/extensions/xep-0332.html
 | 
					 | 
				
			||||||
    Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
 | 
					 | 
				
			||||||
    This file is part of slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from slixmpp import ClientXMPP
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from optparse import OptionParser
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
import getpass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class HTTPOverXMPPClient(ClientXMPP):
 | 
					 | 
				
			||||||
    def __init__(self, jid, password):
 | 
					 | 
				
			||||||
        ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
        self.register_plugin('xep_0332')    # HTTP over XMPP Transport
 | 
					 | 
				
			||||||
        self.add_event_handler(
 | 
					 | 
				
			||||||
            'session_start', self.session_start, threaded=True
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.add_event_handler('http_request', self.http_request_received)
 | 
					 | 
				
			||||||
        self.add_event_handler('http_response', self.http_response_received)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def http_request_received(self, iq):
 | 
					 | 
				
			||||||
        pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def http_response_received(self, iq):
 | 
					 | 
				
			||||||
        print('HTTP Response Received : %s' % iq)
 | 
					 | 
				
			||||||
        print('From    : %s' %  iq['from'])
 | 
					 | 
				
			||||||
        print('To      : %s' % iq['to'])
 | 
					 | 
				
			||||||
        print('Type    : %s' % iq['type'])
 | 
					 | 
				
			||||||
        print('Headers : %s' % iq['resp']['headers'])
 | 
					 | 
				
			||||||
        print('Code    : %s' % iq['resp']['code'])
 | 
					 | 
				
			||||||
        print('Message : %s' % iq['resp']['message'])
 | 
					 | 
				
			||||||
        print('Data    : %s' % iq['resp']['data'])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def session_start(self, event):
 | 
					 | 
				
			||||||
        # TODO: Fill in the blanks
 | 
					 | 
				
			||||||
        self['xep_0332'].send_request(
 | 
					 | 
				
			||||||
            to='?', method='?', resource='?', headers={}
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        self.disconnect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    #
 | 
					 | 
				
			||||||
    # NOTE: To run this example, fill up the blanks in session_start() and
 | 
					 | 
				
			||||||
    #       use the following command.
 | 
					 | 
				
			||||||
    #
 | 
					 | 
				
			||||||
    # ./http_over_xmpp.py -J <jid> -P <pwd> -i <ip> -p <port> [-v]
 | 
					 | 
				
			||||||
    #
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    parser = OptionParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_option(
 | 
					 | 
				
			||||||
        '-v', '--verbose', help='set logging to DEBUG', action='store_const',
 | 
					 | 
				
			||||||
        dest='loglevel', const=logging.DEBUG, default=logging.ERROR
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_option('-J', '--jid', dest='jid', help='JID')
 | 
					 | 
				
			||||||
    parser.add_option('-P', '--password', dest='password', help='Password')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # XMPP server ip and port options.
 | 
					 | 
				
			||||||
    parser.add_option(
 | 
					 | 
				
			||||||
        '-i', '--ipaddr', dest='ipaddr',
 | 
					 | 
				
			||||||
        help='IP Address of the XMPP server', default=None
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    parser.add_option(
 | 
					 | 
				
			||||||
        '-p', '--port', dest='port',
 | 
					 | 
				
			||||||
        help='Port of the XMPP server', default=None
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    opts, args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=opts.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if opts.jid is None:
 | 
					 | 
				
			||||||
        opts.jid = input('Username: ')
 | 
					 | 
				
			||||||
    if opts.password is None:
 | 
					 | 
				
			||||||
        opts.password = getpass.getpass('Password: ')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    xmpp = HTTPOverXMPPClient(opts.jid, opts.password)
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@@ -1,112 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2010  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class IBBReceiver(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A basic example of creating and using an in-band bytestream.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password, filename):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.file = open(filename, 'wb')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The session_start event will be triggered when
 | 
					 | 
				
			||||||
        # the bot establishes its connection with the server
 | 
					 | 
				
			||||||
        # and the XML streams are ready for use. We want to
 | 
					 | 
				
			||||||
        # listen for this event so that we we can initialize
 | 
					 | 
				
			||||||
        # our roster.
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.add_event_handler("ibb_stream_start", self.stream_opened)
 | 
					 | 
				
			||||||
        self.add_event_handler("ibb_stream_data", self.stream_data)
 | 
					 | 
				
			||||||
        self.add_event_handler("ibb_stream_end", self.stream_closed)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def stream_opened(self, stream):
 | 
					 | 
				
			||||||
        print('Stream opened: %s from %s' % (stream.sid, stream.peer_jid))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def stream_data(self, stream):
 | 
					 | 
				
			||||||
        self.file.write(stream.read())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def stream_closed(self, stream):
 | 
					 | 
				
			||||||
        print('Stream closed: %s from %s' % (stream.sid, stream.peer_jid))
 | 
					 | 
				
			||||||
        self.file.close()
 | 
					 | 
				
			||||||
        self.disconnect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-o", "--out", dest="filename",
 | 
					 | 
				
			||||||
                        help="file to save to")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
    if args.filename is None:
 | 
					 | 
				
			||||||
        args.filename = input("File path: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the IBBReceiver and register plugins. Note that while plugins may
 | 
					 | 
				
			||||||
    # have interdependencies, the order in which you register them does
 | 
					 | 
				
			||||||
    # not matter.
 | 
					 | 
				
			||||||
    xmpp = IBBReceiver(args.jid, args.password, args.filename)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0030') # Service Discovery
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0047', {
 | 
					 | 
				
			||||||
        'auto_accept': True
 | 
					 | 
				
			||||||
    }) # In-band Bytestreams
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process(forever=False)
 | 
					 | 
				
			||||||
@@ -1,125 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2010  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import asyncio
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
from slixmpp.exceptions import IqError, IqTimeout
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class IBBSender(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A basic example of creating and using an in-band bytestream.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password, receiver, filename, use_messages=False):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.receiver = receiver
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.file = open(filename, 'rb')
 | 
					 | 
				
			||||||
        self.use_messages = use_messages
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The session_start event will be triggered when
 | 
					 | 
				
			||||||
        # the bot establishes its connection with the server
 | 
					 | 
				
			||||||
        # and the XML streams are ready for use. We want to
 | 
					 | 
				
			||||||
        # listen for this event so that we we can initialize
 | 
					 | 
				
			||||||
        # our roster.
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @asyncio.coroutine
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            # Open the IBB stream in which to write to.
 | 
					 | 
				
			||||||
            stream = yield from self['xep_0047'].open_stream(self.receiver, use_messages=self.use_messages)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # If you want to send in-memory bytes, use stream.sendall() instead.
 | 
					 | 
				
			||||||
            yield from stream.sendfile(self.file, timeout=10)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            # And finally close the stream.
 | 
					 | 
				
			||||||
            yield from stream.close(timeout=10)
 | 
					 | 
				
			||||||
        except (IqError, IqTimeout):
 | 
					 | 
				
			||||||
            print('File transfer errored')
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            print('File transfer finished')
 | 
					 | 
				
			||||||
        finally:
 | 
					 | 
				
			||||||
            self.file.close()
 | 
					 | 
				
			||||||
            self.disconnect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-r", "--receiver", dest="receiver",
 | 
					 | 
				
			||||||
                        help="JID of the receiver")
 | 
					 | 
				
			||||||
    parser.add_argument("-f", "--file", dest="filename",
 | 
					 | 
				
			||||||
                        help="file to send")
 | 
					 | 
				
			||||||
    parser.add_argument("-m", "--use-messages", action="store_true",
 | 
					 | 
				
			||||||
                        help="use messages instead of iqs for file transfer")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
    if args.receiver is None:
 | 
					 | 
				
			||||||
        args.receiver = input("Receiver: ")
 | 
					 | 
				
			||||||
    if args.filename is None:
 | 
					 | 
				
			||||||
        args.filename = input("File path: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the IBBSender and register plugins. Note that while plugins may
 | 
					 | 
				
			||||||
    # have interdependencies, the order in which you register them does
 | 
					 | 
				
			||||||
    # not matter.
 | 
					 | 
				
			||||||
    xmpp = IBBSender(args.jid, args.password, args.receiver, args.filename, args.use_messages)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0030') # Service Discovery
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0047') # In-band Bytestreams
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process(forever=False)
 | 
					 | 
				
			||||||
@@ -1,107 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import sys
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Setup the command line arguments.
 | 
					 | 
				
			||||||
parser = ArgumentParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Output verbosity options.
 | 
					 | 
				
			||||||
parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                    action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                    const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                    action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                    const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# JID and password options.
 | 
					 | 
				
			||||||
parser.add_argument("--oldjid", dest="old_jid",
 | 
					 | 
				
			||||||
                    help="JID of the old account")
 | 
					 | 
				
			||||||
parser.add_argument("--oldpassword", dest="old_password",
 | 
					 | 
				
			||||||
                    help="password of the old account")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
parser.add_argument("--newjid", dest="new_jid",
 | 
					 | 
				
			||||||
                    help="JID of the old account")
 | 
					 | 
				
			||||||
parser.add_argument("--newpassword", dest="new_password",
 | 
					 | 
				
			||||||
                    help="password of the old account")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Setup logging.
 | 
					 | 
				
			||||||
logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                    format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if args.old_jid is None:
 | 
					 | 
				
			||||||
    args.old_jid = input("Old JID: ")
 | 
					 | 
				
			||||||
if args.old_password is None:
 | 
					 | 
				
			||||||
    args.old_password = getpass("Old Password: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if args.new_jid is None:
 | 
					 | 
				
			||||||
    args.new_jid = input("New JID: ")
 | 
					 | 
				
			||||||
if args.new_password is None:
 | 
					 | 
				
			||||||
    args.new_password = getpass("New Password: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
old_xmpp = slixmpp.ClientXMPP(args.old_jid, args.old_password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# If you are connecting to Facebook and wish to use the
 | 
					 | 
				
			||||||
# X-FACEBOOK-PLATFORM authentication mechanism, you will need
 | 
					 | 
				
			||||||
# your API key and an access token. Then you'll set:
 | 
					 | 
				
			||||||
# xmpp.credentials['api_key'] = 'THE_API_KEY'
 | 
					 | 
				
			||||||
# xmpp.credentials['access_token'] = 'THE_ACCESS_TOKEN'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# If you are connecting to MSN, then you will need an
 | 
					 | 
				
			||||||
# access token, and it does not matter what JID you
 | 
					 | 
				
			||||||
# specify other than that the domain is 'messenger.live.com',
 | 
					 | 
				
			||||||
# so '_@messenger.live.com' will work. You can specify
 | 
					 | 
				
			||||||
# the access token as so:
 | 
					 | 
				
			||||||
# xmpp.credentials['access_token'] = 'THE_ACCESS_TOKEN'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# If you are working with an OpenFire server, you may need
 | 
					 | 
				
			||||||
# to adjust the SSL version used:
 | 
					 | 
				
			||||||
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# If you want to verify the SSL certificates offered by a server:
 | 
					 | 
				
			||||||
# xmpp.ca_certs = "path/to/ca/cert"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
roster = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def on_session(event):
 | 
					 | 
				
			||||||
    roster.append(old_xmpp.get_roster())
 | 
					 | 
				
			||||||
    old_xmpp.disconnect()
 | 
					 | 
				
			||||||
old_xmpp.add_event_handler('session_start', on_session)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if old_xmpp.connect():
 | 
					 | 
				
			||||||
    old_xmpp.process(block=True)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if not roster:
 | 
					 | 
				
			||||||
    print('No roster to migrate')
 | 
					 | 
				
			||||||
    sys.exit()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
new_xmpp = slixmpp.ClientXMPP(args.new_jid, args.new_password)
 | 
					 | 
				
			||||||
def on_session2(event):
 | 
					 | 
				
			||||||
    new_xmpp.get_roster()
 | 
					 | 
				
			||||||
    new_xmpp.send_presence()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    logging.info(roster[0])
 | 
					 | 
				
			||||||
    data = roster[0]['roster']['items']
 | 
					 | 
				
			||||||
    logging.info(data)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for jid, item in data.items():
 | 
					 | 
				
			||||||
        if item['subscription'] != 'none':
 | 
					 | 
				
			||||||
            new_xmpp.send_presence(ptype='subscribe', pto=jid)
 | 
					 | 
				
			||||||
        new_xmpp.update_roster(jid,
 | 
					 | 
				
			||||||
                name = item['name'],
 | 
					 | 
				
			||||||
                groups = item['groups'])
 | 
					 | 
				
			||||||
    new_xmpp.disconnect()
 | 
					 | 
				
			||||||
new_xmpp.add_event_handler('session_start', on_session2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
new_xmpp.connect()
 | 
					 | 
				
			||||||
new_xmpp.process()
 | 
					 | 
				
			||||||
							
								
								
									
										169
									
								
								examples/muc.py
									
									
									
									
									
								
							
							
						
						
									
										169
									
								
								examples/muc.py
									
									
									
									
									
								
							@@ -1,169 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2010  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class MUCBot(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A simple Slixmpp bot that will greets those
 | 
					 | 
				
			||||||
    who enter the room, and acknowledge any messages
 | 
					 | 
				
			||||||
    that mentions the bot's nickname.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password, room, nick):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.room = room
 | 
					 | 
				
			||||||
        self.nick = nick
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The session_start event will be triggered when
 | 
					 | 
				
			||||||
        # the bot establishes its connection with the server
 | 
					 | 
				
			||||||
        # and the XML streams are ready for use. We want to
 | 
					 | 
				
			||||||
        # listen for this event so that we we can initialize
 | 
					 | 
				
			||||||
        # our roster.
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The groupchat_message event is triggered whenever a message
 | 
					 | 
				
			||||||
        # stanza is received from any chat room. If you also also
 | 
					 | 
				
			||||||
        # register a handler for the 'message' event, MUC messages
 | 
					 | 
				
			||||||
        # will be processed by both handlers.
 | 
					 | 
				
			||||||
        self.add_event_handler("groupchat_message", self.muc_message)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The groupchat_presence event is triggered whenever a
 | 
					 | 
				
			||||||
        # presence stanza is received from any chat room, including
 | 
					 | 
				
			||||||
        # any presences you send yourself. To limit event handling
 | 
					 | 
				
			||||||
        # to a single room, use the events muc::room@server::presence,
 | 
					 | 
				
			||||||
        # muc::room@server::got_online, or muc::room@server::got_offline.
 | 
					 | 
				
			||||||
        self.add_event_handler("muc::%s::got_online" % self.room,
 | 
					 | 
				
			||||||
                               self.muc_online)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.plugin['xep_0045'].joinMUC(self.room,
 | 
					 | 
				
			||||||
                                        self.nick,
 | 
					 | 
				
			||||||
                                        # If a room password is needed, use:
 | 
					 | 
				
			||||||
                                        # password=the_room_password,
 | 
					 | 
				
			||||||
                                        wait=True)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def muc_message(self, msg):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process incoming message stanzas from any chat room. Be aware
 | 
					 | 
				
			||||||
        that if you also have any handlers for the 'message' event,
 | 
					 | 
				
			||||||
        message stanzas may be processed by both handlers, so check
 | 
					 | 
				
			||||||
        the 'type' attribute when using a 'message' event handler.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Whenever the bot's nickname is mentioned, respond to
 | 
					 | 
				
			||||||
        the message.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        IMPORTANT: Always check that a message is not from yourself,
 | 
					 | 
				
			||||||
                   otherwise you will create an infinite loop responding
 | 
					 | 
				
			||||||
                   to your own messages.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        This handler will reply to messages that mention
 | 
					 | 
				
			||||||
        the bot's nickname.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            msg -- The received message stanza. See the documentation
 | 
					 | 
				
			||||||
                   for stanza objects and the Message stanza to see
 | 
					 | 
				
			||||||
                   how it may be used.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if msg['mucnick'] != self.nick and self.nick in msg['body']:
 | 
					 | 
				
			||||||
            self.send_message(mto=msg['from'].bare,
 | 
					 | 
				
			||||||
                              mbody="I heard that, %s." % msg['mucnick'],
 | 
					 | 
				
			||||||
                              mtype='groupchat')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def muc_online(self, presence):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process a presence stanza from a chat room. In this case,
 | 
					 | 
				
			||||||
        presences from users that have just come online are
 | 
					 | 
				
			||||||
        handled by sending a welcome message that includes
 | 
					 | 
				
			||||||
        the user's nickname and role in the room.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            presence -- The received presence stanza. See the
 | 
					 | 
				
			||||||
                        documentation for the Presence stanza
 | 
					 | 
				
			||||||
                        to see how else it may be used.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        if presence['muc']['nick'] != self.nick:
 | 
					 | 
				
			||||||
            self.send_message(mto=presence['from'].bare,
 | 
					 | 
				
			||||||
                              mbody="Hello, %s %s" % (presence['muc']['role'],
 | 
					 | 
				
			||||||
                                                      presence['muc']['nick']),
 | 
					 | 
				
			||||||
                              mtype='groupchat')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-r", "--room", dest="room",
 | 
					 | 
				
			||||||
                        help="MUC room to join")
 | 
					 | 
				
			||||||
    parser.add_argument("-n", "--nick", dest="nick",
 | 
					 | 
				
			||||||
                        help="MUC nickname")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
    if args.room is None:
 | 
					 | 
				
			||||||
        args.room = input("MUC room: ")
 | 
					 | 
				
			||||||
    if args.nick is None:
 | 
					 | 
				
			||||||
        args.nick = input("MUC nickname: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the MUCBot and register plugins. Note that while plugins may
 | 
					 | 
				
			||||||
    # have interdependencies, the order in which you register them does
 | 
					 | 
				
			||||||
    # not matter.
 | 
					 | 
				
			||||||
    xmpp = MUCBot(args.jid, args.password, args.room, args.nick)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0030') # Service Discovery
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0045') # Multi-User Chat
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0199') # XMPP Ping
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
							
								
								
									
										114
									
								
								examples/ping.py
									
									
									
									
									
								
							
							
						
						
									
										114
									
								
								examples/ping.py
									
									
									
									
									
								
							@@ -1,114 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2010  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
from slixmpp.exceptions import IqError, IqTimeout
 | 
					 | 
				
			||||||
from slixmpp import asyncio
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PingTest(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A simple Slixmpp bot that will send a ping request
 | 
					 | 
				
			||||||
    to a given JID.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password, pingjid):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
        if pingjid is None:
 | 
					 | 
				
			||||||
            pingjid = self.boundjid.bare
 | 
					 | 
				
			||||||
        self.pingjid = pingjid
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The session_start event will be triggered when
 | 
					 | 
				
			||||||
        # the bot establishes its connection with the server
 | 
					 | 
				
			||||||
        # and the XML streams are ready for use. We want to
 | 
					 | 
				
			||||||
        # listen for this event so that we we can initialize
 | 
					 | 
				
			||||||
        # our roster.
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @asyncio.coroutine
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            rtt = yield from self['xep_0199'].ping(self.pingjid,
 | 
					 | 
				
			||||||
                                                   timeout=10)
 | 
					 | 
				
			||||||
            logging.info("Success! RTT: %s", rtt)
 | 
					 | 
				
			||||||
        except IqError as e:
 | 
					 | 
				
			||||||
            logging.info("Error pinging %s: %s",
 | 
					 | 
				
			||||||
                    self.pingjid,
 | 
					 | 
				
			||||||
                    e.iq['error']['condition'])
 | 
					 | 
				
			||||||
        except IqTimeout:
 | 
					 | 
				
			||||||
            logging.info("No response from %s", self.pingjid)
 | 
					 | 
				
			||||||
        finally:
 | 
					 | 
				
			||||||
            self.disconnect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-t", "--pingto", help="set jid to ping",
 | 
					 | 
				
			||||||
                        dest="pingjid", default=None)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the PingTest and register plugins. Note that while plugins may
 | 
					 | 
				
			||||||
    # have interdependencies, the order in which you register them does
 | 
					 | 
				
			||||||
    # not matter.
 | 
					 | 
				
			||||||
    xmpp = PingTest(args.jid, args.password, args.pingjid)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0030')  # Service Discovery
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0004')  # Data Forms
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0060')  # PubSub
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0199')  # XMPP Ping
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
@@ -1,135 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2010  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class EchoBot(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A simple Slixmpp bot that will echo messages it
 | 
					 | 
				
			||||||
    receives, along with a short thank you message.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The session_start event will be triggered when
 | 
					 | 
				
			||||||
        # the bot establishes its connection with the server
 | 
					 | 
				
			||||||
        # and the XML streams are ready for use. We want to
 | 
					 | 
				
			||||||
        # listen for this event so that we we can initialize
 | 
					 | 
				
			||||||
        # our roster.
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The message event is triggered whenever a message
 | 
					 | 
				
			||||||
        # stanza is received. Be aware that that includes
 | 
					 | 
				
			||||||
        # MUC messages and error messages.
 | 
					 | 
				
			||||||
        self.add_event_handler("message", self.message)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def message(self, msg):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process incoming message stanzas. Be aware that this also
 | 
					 | 
				
			||||||
        includes MUC messages and error messages. It is usually
 | 
					 | 
				
			||||||
        a good idea to check the messages's type before processing
 | 
					 | 
				
			||||||
        or sending replies.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            msg -- The received message stanza. See the documentation
 | 
					 | 
				
			||||||
                   for stanza objects and the Message stanza to see
 | 
					 | 
				
			||||||
                   how it may be used.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        msg.reply("Thanks for sending\n%(body)s" % msg).send()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
    parser.add_argument("--phost", dest="proxy_host",
 | 
					 | 
				
			||||||
                        help="Proxy hostname")
 | 
					 | 
				
			||||||
    parser.add_argument("--pport", dest="proxy_port",
 | 
					 | 
				
			||||||
                        help="Proxy port")
 | 
					 | 
				
			||||||
    parser.add_argument("--puser", dest="proxy_user",
 | 
					 | 
				
			||||||
                        help="Proxy username")
 | 
					 | 
				
			||||||
    parser.add_argument("--ppass", dest="proxy_pass",
 | 
					 | 
				
			||||||
                        help="Proxy password")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
    if args.proxy_host is None:
 | 
					 | 
				
			||||||
        args.proxy_host = input("Proxy host: ")
 | 
					 | 
				
			||||||
    if args.proxy_port is None:
 | 
					 | 
				
			||||||
        args.proxy_port = input("Proxy port: ")
 | 
					 | 
				
			||||||
    if args.proxy_user is None:
 | 
					 | 
				
			||||||
        args.proxy_user = input("Proxy username: ")
 | 
					 | 
				
			||||||
    if args.proxy_pass is None and args.proxy_user:
 | 
					 | 
				
			||||||
        args.proxy_pass = getpass("Proxy password: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the EchoBot and register plugins. Note that while plugins may
 | 
					 | 
				
			||||||
    # have interdependencies, the order in which you register them does
 | 
					 | 
				
			||||||
    # not matter.
 | 
					 | 
				
			||||||
    xmpp = EchoBot(args.jid, args.password)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0030') # Service Discovery
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0004') # Data Forms
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0060') # PubSub
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0199') # XMPP Ping
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    xmpp.use_proxy = True
 | 
					 | 
				
			||||||
    xmpp.proxy_config = {
 | 
					 | 
				
			||||||
        'host': args.proxy_host,
 | 
					 | 
				
			||||||
        'port': int(args.proxy_port),
 | 
					 | 
				
			||||||
        'username': args.proxy_user,
 | 
					 | 
				
			||||||
        'password': args.proxy_pass}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
@@ -1,166 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import asyncio
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
from slixmpp.exceptions import XMPPError
 | 
					 | 
				
			||||||
from slixmpp.xmlstream import ET, tostring
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PubsubClient(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password, server,
 | 
					 | 
				
			||||||
                       node=None, action='nodes', data=''):
 | 
					 | 
				
			||||||
        super(PubsubClient, self).__init__(jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.register_plugin('xep_0030')
 | 
					 | 
				
			||||||
        self.register_plugin('xep_0059')
 | 
					 | 
				
			||||||
        self.register_plugin('xep_0060')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.actions = ['nodes', 'create', 'delete',
 | 
					 | 
				
			||||||
                        'publish', 'get', 'retract',
 | 
					 | 
				
			||||||
                        'purge', 'subscribe', 'unsubscribe']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.action = action
 | 
					 | 
				
			||||||
        self.node = node
 | 
					 | 
				
			||||||
        self.data = data
 | 
					 | 
				
			||||||
        self.pubsub_server = server
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.add_event_handler('session_start', self.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @asyncio.coroutine
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            yield from getattr(self, self.action)()
 | 
					 | 
				
			||||||
        except:
 | 
					 | 
				
			||||||
            logging.error('Could not execute: %s', self.action)
 | 
					 | 
				
			||||||
        self.disconnect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def nodes(self):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            result = yield from self['xep_0060'].get_nodes(self.pubsub_server, self.node)
 | 
					 | 
				
			||||||
            for item in result['disco_items']['items']:
 | 
					 | 
				
			||||||
                logging.info('  - %s', str(item))
 | 
					 | 
				
			||||||
        except XMPPError as error:
 | 
					 | 
				
			||||||
            logging.error('Could not retrieve node list: %s', error.format())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def create(self):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            yield from self['xep_0060'].create_node(self.pubsub_server, self.node)
 | 
					 | 
				
			||||||
            logging.info('Created node %s', self.node)
 | 
					 | 
				
			||||||
        except XMPPError as error:
 | 
					 | 
				
			||||||
            logging.error('Could not create node %s: %s', self.node, error.format())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def delete(self):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            yield from self['xep_0060'].delete_node(self.pubsub_server, self.node)
 | 
					 | 
				
			||||||
            logging.info('Deleted node %s', self.node)
 | 
					 | 
				
			||||||
        except XMPPError as error:
 | 
					 | 
				
			||||||
            logging.error('Could not delete node %s: %s', self.node, error.format())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def publish(self):
 | 
					 | 
				
			||||||
        payload = ET.fromstring("<test xmlns='test'>%s</test>" % self.data)
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            result = yield from self['xep_0060'].publish(self.pubsub_server, self.node, payload=payload)
 | 
					 | 
				
			||||||
            logging.info('Published at item id: %s', result['pubsub']['publish']['item']['id'])
 | 
					 | 
				
			||||||
        except XMPPError as error:
 | 
					 | 
				
			||||||
            logging.error('Could not publish to %s: %s', self.node, error.format())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get(self):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            result = yield from self['xep_0060'].get_item(self.pubsub_server, self.node, self.data)
 | 
					 | 
				
			||||||
            for item in result['pubsub']['items']['substanzas']:
 | 
					 | 
				
			||||||
                logging.info('Retrieved item %s: %s', item['id'], tostring(item['payload']))
 | 
					 | 
				
			||||||
        except XMPPError as error:
 | 
					 | 
				
			||||||
            logging.error('Could not retrieve item %s from node %s: %s', self.data, self.node, error.format())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def retract(self):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            yield from self['xep_0060'].retract(self.pubsub_server, self.node, self.data)
 | 
					 | 
				
			||||||
            logging.info('Retracted item %s from node %s', self.data, self.node)
 | 
					 | 
				
			||||||
        except XMPPError as error:
 | 
					 | 
				
			||||||
            logging.error('Could not retract item %s from node %s: %s', self.data, self.node, error.format())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def purge(self):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            yield from self['xep_0060'].purge(self.pubsub_server, self.node)
 | 
					 | 
				
			||||||
            logging.info('Purged all items from node %s', self.node)
 | 
					 | 
				
			||||||
        except XMPPError as error:
 | 
					 | 
				
			||||||
            logging.error('Could not purge items from node %s: %s', self.node, error.format())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def subscribe(self):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            iq = yield from self['xep_0060'].subscribe(self.pubsub_server, self.node)
 | 
					 | 
				
			||||||
            subscription = iq['pubsub']['subscription']
 | 
					 | 
				
			||||||
            logging.info('Subscribed %s to node %s', subscription['jid'], subscription['node'])
 | 
					 | 
				
			||||||
        except XMPPError as error:
 | 
					 | 
				
			||||||
            logging.error('Could not subscribe %s to node %s: %s', self.boundjid.bare, self.node, error.format())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def unsubscribe(self):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            yield from self['xep_0060'].unsubscribe(self.pubsub_server, self.node)
 | 
					 | 
				
			||||||
            logging.info('Unsubscribed %s from node %s', self.boundjid.bare, self.node)
 | 
					 | 
				
			||||||
        except XMPPError as error:
 | 
					 | 
				
			||||||
            logging.error('Could not unsubscribe %s from node %s: %s', self.boundjid.bare, self.node, error.format())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
    parser.version = '%%prog 0.1'
 | 
					 | 
				
			||||||
    parser.usage = "Usage: %%prog [options] <jid> " + \
 | 
					 | 
				
			||||||
                             'nodes|create|delete|purge|subscribe|unsubscribe|publish|retract|get' + \
 | 
					 | 
				
			||||||
                             ' [<node> <data>]'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    parser.add_argument("-q","--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const",
 | 
					 | 
				
			||||||
                        dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR,
 | 
					 | 
				
			||||||
                        default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d","--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const",
 | 
					 | 
				
			||||||
                        dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG,
 | 
					 | 
				
			||||||
                        default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    parser.add_argument("server")
 | 
					 | 
				
			||||||
    parser.add_argument("action", choices=["nodes", "create", "delete", "purge", "subscribe", "unsubscribe", "publish", "retract", "get"])
 | 
					 | 
				
			||||||
    parser.add_argument("node", nargs='?')
 | 
					 | 
				
			||||||
    parser.add_argument("data", nargs='?')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the Pubsub client
 | 
					 | 
				
			||||||
    xmpp = PubsubClient(args.jid, args.password,
 | 
					 | 
				
			||||||
                        server=args.server,
 | 
					 | 
				
			||||||
                        node=args.node,
 | 
					 | 
				
			||||||
                        action=args.action,
 | 
					 | 
				
			||||||
                        data=args.data)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process(forever=False)
 | 
					 | 
				
			||||||
@@ -1,122 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
from slixmpp.xmlstream import ET, tostring
 | 
					 | 
				
			||||||
from slixmpp.xmlstream.matcher import StanzaPath
 | 
					 | 
				
			||||||
from slixmpp.xmlstream.handler import Callback
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PubsubEvents(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password):
 | 
					 | 
				
			||||||
        super(PubsubEvents, self).__init__(jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.register_plugin('xep_0030')
 | 
					 | 
				
			||||||
        self.register_plugin('xep_0059')
 | 
					 | 
				
			||||||
        self.register_plugin('xep_0060')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.add_event_handler('session_start', self.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Some services may require configuration to allow
 | 
					 | 
				
			||||||
        # sending delete, configuration, or subscription  events.
 | 
					 | 
				
			||||||
        self.add_event_handler('pubsub_publish', self._publish)
 | 
					 | 
				
			||||||
        self.add_event_handler('pubsub_retract', self._retract)
 | 
					 | 
				
			||||||
        self.add_event_handler('pubsub_purge', self._purge)
 | 
					 | 
				
			||||||
        self.add_event_handler('pubsub_delete', self._delete)
 | 
					 | 
				
			||||||
        self.add_event_handler('pubsub_config', self._config)
 | 
					 | 
				
			||||||
        self.add_event_handler('pubsub_subscription', self._subscription)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Want to use nicer, more specific pubsub event names?
 | 
					 | 
				
			||||||
        # self['xep_0060'].map_node_event('node_name', 'event_prefix')
 | 
					 | 
				
			||||||
        # self.add_event_handler('event_prefix_publish', handler)
 | 
					 | 
				
			||||||
        # self.add_event_handler('event_prefix_retract', handler)
 | 
					 | 
				
			||||||
        # self.add_event_handler('event_prefix_purge', handler)
 | 
					 | 
				
			||||||
        # self.add_event_handler('event_prefix_delete', handler)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _publish(self, msg):
 | 
					 | 
				
			||||||
        """Handle receiving a publish item event."""
 | 
					 | 
				
			||||||
        print('Published item %s to %s:' % (
 | 
					 | 
				
			||||||
            msg['pubsub_event']['items']['item']['id'],
 | 
					 | 
				
			||||||
            msg['pubsub_event']['items']['node']))
 | 
					 | 
				
			||||||
        data = msg['pubsub_event']['items']['item']['payload']
 | 
					 | 
				
			||||||
        if data is not None:
 | 
					 | 
				
			||||||
            print(tostring(data))
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            print('No item content')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _retract(self, msg):
 | 
					 | 
				
			||||||
        """Handle receiving a retract item event."""
 | 
					 | 
				
			||||||
        print('Retracted item %s from %s' % (
 | 
					 | 
				
			||||||
            msg['pubsub_event']['items']['retract']['id'],
 | 
					 | 
				
			||||||
            msg['pubsub_event']['items']['node']))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _purge(self, msg):
 | 
					 | 
				
			||||||
        """Handle receiving a node purge event."""
 | 
					 | 
				
			||||||
        print('Purged all items from %s' % (
 | 
					 | 
				
			||||||
            msg['pubsub_event']['purge']['node']))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _delete(self, msg):
 | 
					 | 
				
			||||||
        """Handle receiving a node deletion event."""
 | 
					 | 
				
			||||||
        print('Deleted node %s' % (
 | 
					 | 
				
			||||||
           msg['pubsub_event']['delete']['node']))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _config(self, msg):
 | 
					 | 
				
			||||||
        """Handle receiving a node configuration event."""
 | 
					 | 
				
			||||||
        print('Configured node %s:' % (
 | 
					 | 
				
			||||||
            msg['pubsub_event']['configuration']['node']))
 | 
					 | 
				
			||||||
        print(msg['pubsub_event']['configuration']['form'])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _subscription(self, msg):
 | 
					 | 
				
			||||||
        """Handle receiving a node subscription event."""
 | 
					 | 
				
			||||||
        print('Subscription change for node %s:' % (
 | 
					 | 
				
			||||||
            msg['pubsub_event']['subscription']['node']))
 | 
					 | 
				
			||||||
        print(msg['pubsub_event']['subscription'])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    logging.info("Run this in conjunction with the pubsub_client.py " + \
 | 
					 | 
				
			||||||
                 "example to watch events happen as you give commands.")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the PubsubEvents listener
 | 
					 | 
				
			||||||
    xmpp = PubsubEvents(args.jid, args.password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
@@ -1,148 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2010  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
from slixmpp.exceptions import IqError, IqTimeout
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class RegisterBot(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A basic bot that will attempt to register an account
 | 
					 | 
				
			||||||
    with an XMPP server.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    NOTE: This follows the very basic registration workflow
 | 
					 | 
				
			||||||
          from XEP-0077. More advanced server registration
 | 
					 | 
				
			||||||
          workflows will need to check for data forms, etc.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The session_start event will be triggered when
 | 
					 | 
				
			||||||
        # the bot establishes its connection with the server
 | 
					 | 
				
			||||||
        # and the XML streams are ready for use. We want to
 | 
					 | 
				
			||||||
        # listen for this event so that we we can initialize
 | 
					 | 
				
			||||||
        # our roster.
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # The register event provides an Iq result stanza with
 | 
					 | 
				
			||||||
        # a registration form from the server. This may include
 | 
					 | 
				
			||||||
        # the basic registration fields, a data form, an
 | 
					 | 
				
			||||||
        # out-of-band URL, or any combination. For more advanced
 | 
					 | 
				
			||||||
        # cases, you will need to examine the fields provided
 | 
					 | 
				
			||||||
        # and respond accordingly. Slixmpp provides plugins
 | 
					 | 
				
			||||||
        # for data forms and OOB links that will make that easier.
 | 
					 | 
				
			||||||
        self.add_event_handler("register", self.register)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
        self.get_roster()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # We're only concerned about registering, so nothing more to do here.
 | 
					 | 
				
			||||||
        self.disconnect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def register(self, iq):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Fill out and submit a registration form.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        The form may be composed of basic registration fields, a data form,
 | 
					 | 
				
			||||||
        an out-of-band link, or any combination thereof. Data forms and OOB
 | 
					 | 
				
			||||||
        links can be checked for as so:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if iq.match('iq/register/form'):
 | 
					 | 
				
			||||||
            # do stuff with data form
 | 
					 | 
				
			||||||
            # iq['register']['form']['fields']
 | 
					 | 
				
			||||||
        if iq.match('iq/register/oob'):
 | 
					 | 
				
			||||||
            # do stuff with OOB URL
 | 
					 | 
				
			||||||
            # iq['register']['oob']['url']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        To get the list of basic registration fields, you can use:
 | 
					 | 
				
			||||||
            iq['register']['fields']
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        resp = self.Iq()
 | 
					 | 
				
			||||||
        resp['type'] = 'set'
 | 
					 | 
				
			||||||
        resp['register']['username'] = self.boundjid.user
 | 
					 | 
				
			||||||
        resp['register']['password'] = self.password
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            yield from resp.send()
 | 
					 | 
				
			||||||
            logging.info("Account created for %s!" % self.boundjid)
 | 
					 | 
				
			||||||
        except IqError as e:
 | 
					 | 
				
			||||||
            logging.error("Could not register account: %s" %
 | 
					 | 
				
			||||||
                    e.iq['error']['text'])
 | 
					 | 
				
			||||||
            self.disconnect()
 | 
					 | 
				
			||||||
        except IqTimeout:
 | 
					 | 
				
			||||||
            logging.error("No response from server.")
 | 
					 | 
				
			||||||
            self.disconnect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Output verbosity options.
 | 
					 | 
				
			||||||
    parser.add_argument("-q", "--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR, default=logging.INFO)
 | 
					 | 
				
			||||||
    parser.add_argument("-d", "--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const", dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG, default=logging.INFO)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup the RegisterBot and register plugins. Note that while plugins may
 | 
					 | 
				
			||||||
    # have interdependencies, the order in which you register them does
 | 
					 | 
				
			||||||
    # not matter.
 | 
					 | 
				
			||||||
    xmpp = RegisterBot(args.jid, args.password)
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0030') # Service Discovery
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0004') # Data forms
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0066') # Out-of-band Data
 | 
					 | 
				
			||||||
    xmpp.register_plugin('xep_0077') # In-band Registration
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Some servers don't advertise support for inband registration, even
 | 
					 | 
				
			||||||
    # though they allow it. If this applies to your server, use:
 | 
					 | 
				
			||||||
    xmpp['xep_0077'].force_registration = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
@@ -1,142 +0,0 @@
 | 
				
			|||||||
#!/usr/bin/env python3
 | 
					 | 
				
			||||||
# -*- coding: utf-8 -*-
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
    Slixmpp: The Slick XMPP Library
 | 
					 | 
				
			||||||
    Copyright (C) 2011  Nathanael C. Fritz
 | 
					 | 
				
			||||||
    This file is part of Slixmpp.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    See the file LICENSE for copying permission.
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import logging
 | 
					 | 
				
			||||||
from getpass import getpass
 | 
					 | 
				
			||||||
from argparse import ArgumentParser
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import slixmpp
 | 
					 | 
				
			||||||
from slixmpp.exceptions import IqError, IqTimeout
 | 
					 | 
				
			||||||
from slixmpp.xmlstream.asyncio import asyncio
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class RosterBrowser(slixmpp.ClientXMPP):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    A basic script for dumping a client's roster to
 | 
					 | 
				
			||||||
    the command line.
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, jid, password):
 | 
					 | 
				
			||||||
        slixmpp.ClientXMPP.__init__(self, jid, password)
 | 
					 | 
				
			||||||
        # The session_start event will be triggered when
 | 
					 | 
				
			||||||
        # the bot establishes its connection with the server
 | 
					 | 
				
			||||||
        # and the XML streams are ready for use. We want to
 | 
					 | 
				
			||||||
        # listen for this event so that we we can initialize
 | 
					 | 
				
			||||||
        # our roster.
 | 
					 | 
				
			||||||
        self.add_event_handler("session_start", self.start)
 | 
					 | 
				
			||||||
        self.add_event_handler("changed_status", self.wait_for_presences)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.received = set()
 | 
					 | 
				
			||||||
        self.presences_received = asyncio.Event()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @asyncio.coroutine
 | 
					 | 
				
			||||||
    def start(self, event):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Process the session_start event.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Typical actions for the session_start event are
 | 
					 | 
				
			||||||
        requesting the roster and broadcasting an initial
 | 
					 | 
				
			||||||
        presence stanza.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Arguments:
 | 
					 | 
				
			||||||
            event -- An empty dictionary. The session_start
 | 
					 | 
				
			||||||
                     event does not provide any additional
 | 
					 | 
				
			||||||
                     data.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        future = asyncio.Future()
 | 
					 | 
				
			||||||
        def callback(result):
 | 
					 | 
				
			||||||
            future.set_result(None)
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            self.get_roster(callback=callback)
 | 
					 | 
				
			||||||
            yield from future
 | 
					 | 
				
			||||||
        except IqError as err:
 | 
					 | 
				
			||||||
            print('Error: %s' % err.iq['error']['condition'])
 | 
					 | 
				
			||||||
        except IqTimeout:
 | 
					 | 
				
			||||||
            print('Error: Request timed out')
 | 
					 | 
				
			||||||
        self.send_presence()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        print('Waiting for presence updates...\n')
 | 
					 | 
				
			||||||
        yield from asyncio.sleep(10)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        print('Roster for %s' % self.boundjid.bare)
 | 
					 | 
				
			||||||
        groups = self.client_roster.groups()
 | 
					 | 
				
			||||||
        for group in groups:
 | 
					 | 
				
			||||||
            print('\n%s' % group)
 | 
					 | 
				
			||||||
            print('-' * 72)
 | 
					 | 
				
			||||||
            for jid in groups[group]:
 | 
					 | 
				
			||||||
                sub = self.client_roster[jid]['subscription']
 | 
					 | 
				
			||||||
                name = self.client_roster[jid]['name']
 | 
					 | 
				
			||||||
                if self.client_roster[jid]['name']:
 | 
					 | 
				
			||||||
                    print(' %s (%s) [%s]' % (name, jid, sub))
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    print(' %s [%s]' % (jid, sub))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                connections = self.client_roster.presence(jid)
 | 
					 | 
				
			||||||
                for res, pres in connections.items():
 | 
					 | 
				
			||||||
                    show = 'available'
 | 
					 | 
				
			||||||
                    if pres['show']:
 | 
					 | 
				
			||||||
                        show = pres['show']
 | 
					 | 
				
			||||||
                    print('   - %s (%s)' % (res, show))
 | 
					 | 
				
			||||||
                    if pres['status']:
 | 
					 | 
				
			||||||
                        print('       %s' % pres['status'])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.disconnect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def wait_for_presences(self, pres):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Track how many roster entries have received presence updates.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        self.received.add(pres['from'].bare)
 | 
					 | 
				
			||||||
        if len(self.received) >= len(self.client_roster.keys()):
 | 
					 | 
				
			||||||
            self.presences_received.set()
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            self.presences_received.clear()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    # Setup the command line arguments.
 | 
					 | 
				
			||||||
    parser = ArgumentParser()
 | 
					 | 
				
			||||||
    parser.add_argument("-q","--quiet", help="set logging to ERROR",
 | 
					 | 
				
			||||||
                        action="store_const",
 | 
					 | 
				
			||||||
                        dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.ERROR,
 | 
					 | 
				
			||||||
                        default=logging.ERROR)
 | 
					 | 
				
			||||||
    parser.add_argument("-d","--debug", help="set logging to DEBUG",
 | 
					 | 
				
			||||||
                        action="store_const",
 | 
					 | 
				
			||||||
                        dest="loglevel",
 | 
					 | 
				
			||||||
                        const=logging.DEBUG,
 | 
					 | 
				
			||||||
                        default=logging.ERROR)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # JID and password options.
 | 
					 | 
				
			||||||
    parser.add_argument("-j", "--jid", dest="jid",
 | 
					 | 
				
			||||||
                        help="JID to use")
 | 
					 | 
				
			||||||
    parser.add_argument("-p", "--password", dest="password",
 | 
					 | 
				
			||||||
                        help="password to use")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    args = parser.parse_args()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Setup logging.
 | 
					 | 
				
			||||||
    logging.basicConfig(level=args.loglevel,
 | 
					 | 
				
			||||||
                        format='%(levelname)-8s %(message)s')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if args.jid is None:
 | 
					 | 
				
			||||||
        args.jid = input("Username: ")
 | 
					 | 
				
			||||||
    if args.password is None:
 | 
					 | 
				
			||||||
        args.password = getpass("Password: ")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    xmpp = RosterBrowser(args.jid, args.password)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # Connect to the XMPP server and start processing XMPP stanzas.
 | 
					 | 
				
			||||||
    xmpp.connect()
 | 
					 | 
				
			||||||
    xmpp.process()
 | 
					 | 
				
			||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user