Compare commits
	
		
			306 Commits
		
	
	
		
			slix-1.1
			...
			slix-1.5.0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 3dcb96d9d8 | ||
|   | 0a7a4c3abe | ||
|   | a4bbc404ed | ||
|   | c3fbc6cb80 | ||
|   | 355d789061 | ||
|   | 47ed67c04e | ||
|   | 34567f450a | ||
|   | 9126bd8392 | ||
|   | 02202f7cd8 | ||
|   | 2add94f5b0 | ||
|   | 5fc757f200 | ||
|   | 98108d0445 | ||
|   | 76f4fb49d6 | ||
|   | 5be46a5e68 | ||
|   | ab9040c30e | ||
|   | a16e2a0f6c | ||
|   | 842aa3be8f | ||
|   | 6c28b49e7f | ||
|   | 621255027d | ||
|   | efe316dc8c | ||
|   | e9a87a0b77 | ||
|   | 85c9967b9c | ||
|   | deb6d4f176 | ||
|   | 7218bb4499 | ||
|   | d85efec7a2 | ||
|   | 115c234527 | ||
|   | a0f5cb6e09 | ||
|   | 110bbf8afc | ||
|   | d97efa0bd8 | ||
|   | 672f1b28f6 | ||
|   | 27d3ae958b | ||
|   | a32794ec35 | ||
|   | aa11ba463e | ||
|   | a83c00e933 | ||
|   | 31f6ef6814 | ||
|   | 9b3874b5df | ||
|   | 0139fb291e | ||
|   | e58988484a | ||
|   | 5d5e5cda19 | ||
|   | 11f707987d | ||
|   | db13794e0f | ||
|   | 37bc1bb9b3 | ||
|   | 9be30e5291 | ||
|   | 9fe20a4056 | ||
|   | 3253d34c0a | ||
|   | fef575ee1a | ||
|   | 540ff89427 | ||
|   | dd8ac8fc87 | ||
|   | 2249d878d1 | ||
|   | 89fa9dc1dd | ||
|   | d7729e8683 | ||
|   | d618f55dea | ||
|   | b0e688eb35 | ||
|   | 0e7176483b | ||
|   | f35569a2c1 | ||
|   | bec6f7c8f3 | ||
|   | 027ce2434d | ||
|   | d57fbb57a2 | ||
|   | 85cd7a9166 | ||
|   | d50d996c68 | ||
|   | 371ad20ca7 | ||
|   | 5f49df6b56 | ||
|   | b50bfb2f34 | ||
|   | b29bb30eb7 | ||
|   | 4435c81d77 | ||
|   | 2638ba2744 | ||
|   | dbc9758311 | ||
|   | 47968963b1 | ||
|   | 4e8800f954 | ||
|   | 40053518aa | ||
|   | 1ee0f72ead | ||
|   | 4bb81228ae | ||
|   | 60a7a5b8df | ||
|   | 946674f424 | ||
|   | 412a9169bd | ||
|   | 72b355de8c | ||
|   | af246dcfe1 | ||
|   | 9612e518fb | ||
|   | fde8264191 | ||
|   | 1cdc656208 | ||
|   | 0042108a67 | ||
|   | 704161a285 | ||
|   | 6b1b58a339 | ||
|   | 4f96e5fa75 | ||
|   | bcb90a653e | ||
|   | 7e435b703d | ||
|   | 2dda6b80d4 | ||
|   | 5629e44710 | ||
|   | 6a06881d8b | ||
|   | 2b666eb1de | ||
|   | 400e7a3903 | ||
|   | fbab3ad214 | ||
|   | 628b357b06 | ||
|   | 88260cc240 | ||
|   | e9f2f503b8 | ||
|   | 696a72247b | ||
|   | 05d76e4b1d | ||
|   | d52d4fbbbe | ||
|   | e53c0fcb30 | ||
|   | 97d68c5196 | ||
|   | b42fafabb4 | ||
|   | 3a44ec8f15 | ||
|   | 93f385562f | ||
|   | 9cab02438b | ||
|   | 74ed50e626 | ||
|   | 9d378c611c | ||
|   | d85d8f4479 | ||
|   | fb75f7cda9 | ||
|   | 41419a2161 | ||
|   | 7cd73b594e | ||
|   | 15c6b775ff | ||
|   | 4b482477e2 | ||
|   | f7e4caadfe | ||
|   | 5f25b0b6a0 | ||
|   | d228bc42ea | ||
|   | ecdc44a601 | ||
|   | 33370e42f1 | ||
|   | 4699861925 | ||
|   | 2d228bdb56 | ||
|   | 570e653ac2 | ||
|   | 282a481059 | ||
|   | f386db380b | ||
|   | 7b87d98fff | ||
|   | 8779d40602 | ||
|   | f0b21c42d5 | ||
|   | e241d4e3c7 | ||
|   | bd22a41a78 | ||
|   | a29a29227a | ||
|   | d4d542b741 | ||
|   | dc4936a6d3 | ||
|   | 897610d819 | ||
|   | d33366badd | ||
|   | 809c500002 | ||
|   | dda4e18b81 | ||
|   | 8c09d932c8 | ||
|   | 31f5e84671 | ||
|   | ad0dc33df9 | ||
|   | 7c3b3827b4 | ||
|   | 9f6fa65139 | ||
|   | 35fa33e3c2 | ||
|   | 86a2f280d2 | ||
|   | 490f15b8fc | ||
|   | 62661ee04f | ||
|   | 37d1f2a6b0 | ||
|   | 20107ad516 | ||
|   | 7738a01311 | ||
|   | a9abed6151 | ||
|   | 0f690d4005 | ||
|   | 59d4420739 | ||
|   | a88f317bbf | ||
|   | 2fc2a88970 | ||
|   | c55e9279ac | ||
|   | 3502480384 | ||
|   | caae713dd6 | ||
|   | df0198abfe | ||
|   | c20f4bf5fa | ||
|   | 9740e93aeb | ||
|   | e7872aaa29 | ||
|   | 037706552c | ||
|   | b881c6729b | ||
|   | 66909aafb3 | ||
|   | cdfb5d56fc | ||
|   | d146ce9fb6 | ||
|   | cb59d60034 | ||
|   | 1d9fe3553e | ||
|   | fe66c022ad | ||
|   | 92ea131721 | ||
|   | dd7f67d10d | ||
|   | c1562b76b2 | ||
|   | 32839f5252 | ||
|   | 80b7cf6ff8 | ||
|   | 128cc2eeb4 | ||
|   | 037912ee89 | ||
|   | 769bc6d3bf | ||
|   | 084d6cb5d9 | ||
|   | 5184713356 | ||
|   | 2f1225bad3 | ||
|   | 841f5a5a5b | ||
|   | 0c6de5e972 | ||
|   | 81dc61c55c | ||
|   | bd63b1ce70 | ||
|   | 29faf114a7 | ||
|   | 94ea8151d4 | ||
|   | 66500ef5fb | ||
|   | 979396bb1e | ||
|   | e177726387 | ||
|   | 20e88fda50 | ||
|   | f252be9b6d | ||
|   | ee98159586 | ||
|   | c6443af29a | ||
|   | d73f56a7af | ||
|   | 7c7f4308c5 | ||
|   | eab8c265f4 | ||
|   | 80b9cd43b1 | ||
|   | af1f9e08ad | ||
|   | e3fd0af9c8 | ||
|   | 27e23672c1 | ||
|   | b38e229359 | ||
|   | 9a563f1425 | ||
|   | 8b6f5953a7 | ||
|   | 2d2a80c73d | ||
|   | 4dfdd5d8e3 | ||
|   | 1994ed3025 | ||
|   | aaa45846d3 | ||
|   | d7ffcb54eb | ||
|   | c33749e57a | ||
|   | e4107d8b4d | ||
|   | da5cb72d3a | ||
|   | c372bd5168 | ||
|   | cabf623131 | ||
|   | ffc240d5b6 | ||
|   | cc4522d9cd | ||
|   | 5bf69dca76 | ||
|   | 59dad12820 | ||
|   | 007c836296 | ||
|   | 3721bf9f6b | ||
|   | 802949eba8 | ||
|   | 24f35e433f | ||
|   | 22664ee7b8 | ||
|   | 6476cfcde5 | ||
|   | 5bb347e884 | ||
|   | eb1251b919 | ||
|   | 820144c40c | ||
|   | 6034df0a78 | ||
|   | df4012e66d | ||
|   | c372f3071a | ||
|   | 829c8b27b6 | ||
|   | fb3ac78bf9 | ||
|   | ffd9436e5c | ||
|   | bbb1344d79 | ||
|   | 457785b286 | ||
|   | 4847f834bd | ||
|   | 53191ff1cf | ||
|   | ffdb6ffd69 | ||
|   | 7560db856b | ||
|   | 63d245ac48 | ||
|   | 7ddd37be29 | ||
|   | a4d3a4a25e | ||
|   | 58bd07628b | ||
|   | 3569038493 | ||
|   | 20c4ff823a | ||
|   | 8a7448a5a1 | ||
|   | d23d8f901e | ||
|   | 391f12eeab | ||
|   | d008988843 | ||
|   | dcacc7d7d5 | ||
|   | c4285961df | ||
|   | 1038f656eb | ||
|   | 8b06aa1146 | ||
|   | 3c7236fe73 | ||
|   | 36824379c3 | ||
|   | a0a37c19ff | ||
|   | 1b5fe57a5e | ||
|   | 5da31db0c7 | ||
|   | f8cea760b6 | ||
|   | 5ef01ecdd1 | ||
|   | 62aafe0ee7 | ||
|   | cf3f36ac52 | ||
|   | b88d2ecd77 | ||
|   | e691850a2b | ||
|   | d4bff8dee6 | ||
|   | 187c350805 | ||
|   | 96d1c26f90 | ||
|   | 46a90749f8 | ||
|   | 0c63a4bbda | ||
|   | e4696e0471 | ||
|   | 8217dc5239 | ||
|   | 2586abc0d3 | ||
|   | 28f84ab3d9 | ||
|   | 813b45aded | ||
|   | 3a9b45e4f2 | ||
|   | b8e091233e | ||
|   | 0edeefd977 | ||
|   | 6ba53cf1ff | ||
|   | d7758eb7f4 | ||
|   | 125336aeee | ||
|   | 7cd1cf32ae | ||
|   | d099e353a4 | ||
|   | 1e4a301c6e | ||
|   | f53b12d227 | ||
|   | e2562dcccf | ||
|   | 7b69ae3738 | ||
|   | ab6df235d7 | ||
|   | 52cd8f4b22 | ||
|   | e28318c271 | ||
|   | 39ee833c29 | ||
|   | 9019e2bc71 | ||
|   | 9208bf5bf1 | ||
|   | f0f1698e46 | ||
|   | eccd7f1c98 | ||
|   | 2587d82af8 | ||
|   | 7ea121b115 | ||
|   | bb81fbbdfc | ||
|   | 1a00a08b7d | ||
|   | 90ea2a3411 | ||
|   | 8fc6814b6d | ||
|   | ffced0ed9a | ||
|   | e7248d9af9 | ||
|   | 6b1a04f59d | ||
|   | 4905407092 | ||
|   | bd6ec10939 | ||
|   | e15e6735f1 | ||
|   | 67afd6a462 | ||
|   | 2e2b97c53b | ||
|   | a35df7fe1f | ||
|   | fbc8562779 | 
							
								
								
									
										13
									
								
								.github/pull_request_template.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								.github/pull_request_template.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| ################ Please use Gitlab instead of Github ################################### | ||||
|  | ||||
| Hello, thank you for contributing to slixmpp! | ||||
|  | ||||
| You’re about to open a pull request on github. However this github repository is not the official place for contributions on slixmpp. | ||||
|  | ||||
| Please open your merge request on https://lab.louiz.org/poezio/slixmpp/ | ||||
|  | ||||
| You should be able to log in there with your github credentials, clone the slixmpp repository in your namespace, push your existing pull request into a new branch, and then open a merge request with one click, within 3 minutes. | ||||
|  | ||||
| This will help us review your contribution, avoid spreading things everywhere and it will even run the tests automatically with your changes. | ||||
|  | ||||
| Thank you. | ||||
							
								
								
									
										21
									
								
								.gitlab-ci.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								.gitlab-ci.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| stages: | ||||
|   - test | ||||
|   - trigger | ||||
|  | ||||
| test: | ||||
|   stage: test | ||||
|   tags: | ||||
|     - docker | ||||
|   image: ubuntu:latest | ||||
|   script: | ||||
|     - apt update | ||||
|     - apt install -y python3 cython3 gpg | ||||
|     - ./run_tests.py | ||||
|  | ||||
| trigger_poezio: | ||||
|   stage: trigger | ||||
|   tags: | ||||
|     - docker | ||||
|   image: appropriate/curl:latest | ||||
|   script: | ||||
|     - curl --request POST -F token="$SLIXMPP_TRIGGER_TOKEN" -F ref=master https://lab.louiz.org/api/v4/projects/18/trigger/pipeline | ||||
| @@ -1,10 +1,7 @@ | ||||
| language: python | ||||
| python: | ||||
|   - "2.6" | ||||
|   - "2.7" | ||||
|   - "3.2" | ||||
|   - "3.3" | ||||
|   - "3.4" | ||||
|   - "3.7" | ||||
|   - "3.8-dev" | ||||
| install: | ||||
|   - "pip install ." | ||||
| script: testall.py | ||||
|   | ||||
							
								
								
									
										14
									
								
								CONTRIBUTING.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								CONTRIBUTING.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| Contributing to the Slixmpp project | ||||
| =================================== | ||||
|  | ||||
| To contribute, the preferred way is to commit your changes on some | ||||
| publicly-available git repository (on a fork `on github | ||||
| <https://github.com/poezio/slixmpp>`_ or on your own repository) and to | ||||
| notify the developers with either: | ||||
|  - a ticket `on the bug tracker <https://lab.louiz.org/poezio/slixmpp/issues/new>`_ | ||||
|  - a pull request on github | ||||
|  - a simple message on `the XMPP MUC <xmpp:slixmpp@muc.poez.io>`_ | ||||
|  | ||||
| Even though Slixmpp’s github repository is just a read-only mirror, we can | ||||
| still be notified of the pull requests and fetch your mirror manually to | ||||
| integrate your changes. | ||||
							
								
								
									
										3
									
								
								INSTALL
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								INSTALL
									
									
									
									
									
								
							| @@ -1,6 +1,7 @@ | ||||
| Pre-requisites: | ||||
| - Python 3.4 | ||||
| - Python 3.7+ | ||||
| - Cython 0.22 and libidn, optionally (making JID faster by compiling the stringprep module) | ||||
| - GnuPG, for testing | ||||
|  | ||||
| Install: | ||||
| > python3 setup.py install | ||||
|   | ||||
							
								
								
									
										25
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								LICENSE
									
									
									
									
									
								
							| @@ -167,28 +167,3 @@ 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. | ||||
|   | ||||
							
								
								
									
										22
									
								
								README.rst
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								README.rst
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| Slixmpp | ||||
| ######### | ||||
|  | ||||
| Slixmpp is an MIT licensed XMPP library for Python 3.4+. It is a fork of | ||||
| Slixmpp is an MIT licensed XMPP library for Python 3.7+. It is a fork of | ||||
| SleekXMPP. | ||||
|  | ||||
| Slixmpp's goals is to only rewrite the core of the library (the low level | ||||
| @@ -12,8 +12,8 @@ Building | ||||
| -------- | ||||
|  | ||||
| Slixmpp can make use of cython to improve performance on critical modules. | ||||
| To do that, cython3 is necessary along with libidn headers. Otherwise, | ||||
| no compilation is needed. Building is done by running setup.py:: | ||||
| To do that, **cython3** is necessary along with **libidn** headers. | ||||
| Otherwise, no compilation is needed. Building is done by running setup.py:: | ||||
|  | ||||
|     python3 setup.py build_ext --inplace | ||||
|  | ||||
| @@ -36,7 +36,7 @@ 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 | ||||
| based project. See the documentation or examples directory for more detailed archetypes for | ||||
| Slixmpp projects:: | ||||
|  | ||||
|     import logging | ||||
| @@ -102,8 +102,18 @@ Slixmpp projects:: | ||||
| Slixmpp Credits | ||||
| --------------- | ||||
|  | ||||
| **Maintainer of the slixmpp fork:** Florent Le Coz | ||||
|     `louiz@louiz.org <xmpp:louiz@louiz.org?message>`_, | ||||
| **Maintainers:** | ||||
|     - Florent Le Coz (`louiz@louiz.org <xmpp:louiz@louiz.org?message>`_), | ||||
|     - Mathieu Pasquet (`mathieui@mathieui.net <xmpp:mathieui@mathieui.net?message>`_), | ||||
|  | ||||
| **Contributors:** | ||||
|     - Emmanuel Gil Peyrot (`Link mauve <xmpp:linkmauve@linkmauve.fr?message>`_) | ||||
|     - Sam Whited (`Sam Whited <mailto:sam@samwhited.com>`_) | ||||
|     - Dan Sully (`Dan Sully <mailto:daniel@electricalrain.com>`_) | ||||
|     - Gasper Zejn (`Gasper Zejn <mailto:zejn@kiberpipa.org>`_) | ||||
|     - Krzysztof Kotlenga (`Krzysztof Kotlenga <mailto:pocek@users.sf.net>`_) | ||||
|     - Tsukasa Hiiragi (`Tsukasa Hiiragi <mailto:bakalolka@gmail.com>`_) | ||||
|     - Maxime Buquet (`pep <xmpp:pep@bouah.net?message>`_) | ||||
|  | ||||
| Credits (SleekXMPP) | ||||
| ------------------- | ||||
|   | ||||
							
								
								
									
										21
									
								
								docs/_static/haiku.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								docs/_static/haiku.css
									
									
									
									
										vendored
									
									
								
							| @@ -408,24 +408,3 @@ div.viewcode-block:target { | ||||
|     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; | ||||
| } | ||||
|   | ||||
							
								
								
									
										1
									
								
								docs/_templates/layout.html
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								docs/_templates/layout.html
									
									
									
									
										vendored
									
									
								
							| @@ -65,6 +65,5 @@ | ||||
|       <div class="bottomnav"> | ||||
|       {{ nav() }} | ||||
|       </div> | ||||
|       <a id="from_andyet" href="http://andyet.net"><h2>From &yet</h2></a> | ||||
| {% endblock %} | ||||
|  | ||||
|   | ||||
| @@ -13,7 +13,7 @@ hides namespaces when able and does not introduce excessive namespace | ||||
| prefixes:: | ||||
|  | ||||
|     >>> from slixmpp.xmlstream.tostring import tostring | ||||
|     >>> from xml.etree import cElementTree as ET | ||||
|     >>> from xml.etree import ElementTree as ET | ||||
|     >>> xml = ET.fromstring('<foo xmlns="bar"><baz /></foo>') | ||||
|     >>> ET.tostring(xml) | ||||
|     '<ns0:foo xmlns:ns0="bar"><ns0:baz /></foo>' | ||||
|   | ||||
							
								
								
									
										13
									
								
								docs/conf.py
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								docs/conf.py
									
									
									
									
									
								
							| @@ -12,12 +12,17 @@ | ||||
| # serve to show the default. | ||||
|  | ||||
| import sys, os | ||||
| import datetime | ||||
|  | ||||
| # 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('..')) | ||||
|  | ||||
| # get version automagically from source tree | ||||
| from slixmpp.version import __version__ as version | ||||
| release = ".".join(version.split(".")[0:2]) | ||||
|  | ||||
| # -- General configuration ----------------------------------------------------- | ||||
|  | ||||
| # If your documentation needs a minimal Sphinx version, state it here. | ||||
| @@ -41,16 +46,18 @@ master_doc = 'index' | ||||
|  | ||||
| # General information about the project. | ||||
| project = u'Slixmpp' | ||||
| copyright = u'2011, Nathan Fritz, Lance Stout' | ||||
| year = datetime.datetime.now().year | ||||
| copyright = u'{}, Nathan Fritz, Lance Stout'.format(year) | ||||
|  | ||||
| # 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. | ||||
| # | ||||
| # auto imported from code! | ||||
| # The short X.Y version. | ||||
| version = '1.1' | ||||
| # version = '1.4' | ||||
| # The full version, including alpha/beta/rc tags. | ||||
| release = '1.1' | ||||
| # release = '1.4.0' | ||||
|  | ||||
| # The language for content autogenerated by Sphinx. Refer to documentation | ||||
| # for a list of supported languages. | ||||
|   | ||||
| @@ -163,7 +163,7 @@ behaviour: | ||||
|         namespace = 'jabber:iq:register' | ||||
|         name = 'query' | ||||
|         plugin_attrib = 'register' | ||||
|         interfaces = set(('username', 'password', 'registered', 'remove')) | ||||
|         interfaces = {'username', 'password', 'registered', 'remove'} | ||||
|         sub_interfaces = interfaces | ||||
|  | ||||
|         def getRegistered(self): | ||||
| @@ -535,10 +535,10 @@ with some additional registration fields implemented. | ||||
|         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')) | ||||
|         interfaces = {'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): | ||||
|   | ||||
| @@ -3,8 +3,9 @@ | ||||
| Differences from SleekXMPP | ||||
| ========================== | ||||
|  | ||||
| **Python 3.4+ only** | ||||
|     slixmpp will only work on python 3.4 and above. | ||||
| **Python 3.7+ only** | ||||
|     slixmpp will work on python 3.7 and above. It may work with previous | ||||
|     versions but we provide no guarantees. | ||||
|  | ||||
| **Stanza copies** | ||||
|     The same stanza object is given through all the handlers; a handler that | ||||
|   | ||||
| @@ -259,8 +259,8 @@ Event Index | ||||
|  | ||||
|         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. | ||||
|         implementations of `XEP-0198: Stream Management <http://xmpp.org/extensions/xep-0198.html>`_ | ||||
|         distinguish between the two events. | ||||
|  | ||||
|         Plugins that maintain session-based state should clear themselves when | ||||
|         this event is fired. | ||||
|   | ||||
| @@ -11,7 +11,7 @@ Create and Run a Server Component | ||||
|     <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>`_. | ||||
| with `Git <https://lab.louiz.org/poezio/slixmpp>`_. | ||||
|  | ||||
| Many XMPP applications eventually graduate to requiring to run as a server | ||||
| component in order to meet scalability requirements. To demonstrate how to | ||||
|   | ||||
| @@ -11,7 +11,7 @@ Slixmpp Quickstart - Echo Bot | ||||
|     <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>`_. | ||||
| with `Git <https://lab.louiz.org/poezio/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 | ||||
| @@ -70,7 +70,7 @@ as well. | ||||
|     class EchoBot(slixmpp.ClientXMPP): | ||||
|  | ||||
|         def __init__(self, jid, password): | ||||
|             super(EchoBot, self).__init__(jid, password) | ||||
|             super().__init__(jid, password) | ||||
|  | ||||
| Handling Session Start | ||||
| ~~~~~~~~~~~~~~~~~~~~~~ | ||||
| @@ -83,7 +83,7 @@ started. To do that, we will register an event handler for the :term:`session_st | ||||
| .. code-block:: python | ||||
|  | ||||
|      def __init__(self, jid, password): | ||||
|         super(EchoBot, self).__init__(jid, password) | ||||
|         super().__init__(jid, password) | ||||
|  | ||||
|         self.add_event_handler('session_start', self.start) | ||||
|  | ||||
| @@ -153,7 +153,7 @@ whenever a messsage is received. | ||||
| .. code-block:: python | ||||
|  | ||||
|      def __init__(self, jid, password): | ||||
|         super(EchoBot, self).__init__(jid, password) | ||||
|         super().__init__(jid, password) | ||||
|  | ||||
|         self.add_event_handler('session_start', self.start) | ||||
|         self.add_event_handler('message', self.message) | ||||
| @@ -329,7 +329,7 @@ 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>`_. | ||||
| can also be found in the Slixmpp `examples directory <https://lab.louiz.org/poezio/slixmpp/tree/master/examples>`_. | ||||
|  | ||||
| .. compound:: | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| .. _mucbot: | ||||
|  | ||||
| ========================= | ||||
| Mulit-User Chat (MUC) Bot | ||||
| Multi-User Chat (MUC) Bot | ||||
| ========================= | ||||
|  | ||||
| .. note:: | ||||
| @@ -11,7 +11,7 @@ Mulit-User Chat (MUC) Bot | ||||
|     <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>`_. | ||||
| from `Git <https://lab.louiz.org/poezio/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 | ||||
| @@ -63,13 +63,13 @@ has been established: | ||||
|     def start(self, event): | ||||
|         self.get_roster() | ||||
|         self.send_presence() | ||||
|         self.plugin['xep_0045'].joinMUC(self.room, | ||||
|                                         self.nick, | ||||
|                                         wait=True) | ||||
|         self.plugin['xep_0045'].join_muc(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. | ||||
| ``join_muc`` method of the MUC plugin. | ||||
|  | ||||
| .. note:: | ||||
|  | ||||
|   | ||||
| @@ -24,7 +24,7 @@ for the JID that will receive our message, and the string content of the message | ||||
|     class SendMsgBot(slixmpp.ClientXMPP): | ||||
|  | ||||
|         def __init__(self, jid, password, recipient, msg): | ||||
|             super(SendMsgBot, self).__init__(jid, password) | ||||
|             super().__init__(jid, password) | ||||
|  | ||||
|             self.recipient = recipient | ||||
|             self.msg = msg | ||||
| @@ -47,11 +47,11 @@ the roster. Next, we want to send our message, and to do that we will use :meth: | ||||
|         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>`. | ||||
| Now, sent stanzas are placed in a queue to pass them to the send thread. | ||||
| :meth:`disconnect <slixmpp.xmlstream.XMLStream.disconnect>` by default will wait for an | ||||
| acknowledgement from the server for at least `2.0` seconds. This time is configurable with | ||||
| the `wait` parameter. If `0.0` is passed for `wait`, :meth:`disconnect | ||||
| <slixmpp.xmlstream.XMLStream.disconnect>` will not close the connection gracefully. | ||||
|  | ||||
| .. code-block:: python | ||||
|  | ||||
| @@ -61,12 +61,12 @@ sent on the wire. To ensure that our message is processed, we use | ||||
|  | ||||
|         self.send_message(mto=self.recipient, mbody=self.msg) | ||||
|  | ||||
|         self.disconnect(wait=True) | ||||
|         self.disconnect() | ||||
|  | ||||
| .. 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>` | ||||
|     can process them, then :meth:`disconnect() <slixmpp.xmlstream.XMLStream.disconnect>` | ||||
|     will block and not disconnect. | ||||
|  | ||||
| Final Product | ||||
|   | ||||
| @@ -61,7 +61,7 @@ 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()``. | ||||
| using the default, static handlers. To do so, use the method ``restore_defaults()``. | ||||
| You may also elect to only convert a given set of actions instead. | ||||
|  | ||||
| Creating a Node Handler | ||||
|   | ||||
| @@ -4,9 +4,9 @@ Slixmpp | ||||
| .. sidebar:: Get the Code | ||||
|  | ||||
|     The latest source code for Slixmpp may be found on the `Git repo | ||||
|     <http://git.poez.io/slixmpp>`_. :: | ||||
|     <https://lab.louiz.org/poezio/slixmpp>`_. :: | ||||
|  | ||||
|         git clone git://git.poez.io/slixmpp | ||||
|         git clone https://lab.louiz.org/poezio/slixmpp | ||||
|  | ||||
|     An XMPP chat room is available for discussing and getting help with slixmpp. | ||||
|  | ||||
| @@ -14,14 +14,14 @@ Slixmpp | ||||
|         `slixmpp@muc.poez.io <xmpp:slixmpp@muc.poez.io?join>`_ | ||||
|  | ||||
|     **Reporting bugs** | ||||
|         You can report bugs at http://dev.louiz.org/projects/slixmpp/issues. | ||||
|         You can report bugs at http://lab.louiz.org/poezio/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 is an :ref:`MIT licensed <license>` XMPP library for Python 3.7+, | ||||
|  | ||||
| Slixmpp's design goals and philosphy are: | ||||
|  | ||||
|   | ||||
| @@ -68,7 +68,7 @@ class CommandBot(slixmpp.ClientXMPP): | ||||
|                        session. Additional, custom data may be saved | ||||
|                        here to persist across handler callbacks. | ||||
|         """ | ||||
|         form = self['xep_0004'].makeForm('form', 'Greeting') | ||||
|         form = self['xep_0004'].make_form('form', 'Greeting') | ||||
|         form['instructions'] = 'Send a custom greeting to a JID' | ||||
|         form.addField(var='greeting', | ||||
|                       ftype='text-single', | ||||
|   | ||||
| @@ -94,7 +94,7 @@ class CommandUserBot(slixmpp.ClientXMPP): | ||||
|         #          label="Your greeting" /> | ||||
|         # </x> | ||||
|  | ||||
|         form = self['xep_0004'].makeForm(ftype='submit') | ||||
|         form = self['xep_0004'].make_form(ftype='submit') | ||||
|         form.addField(var='greeting', | ||||
|                       value=session['greeting']) | ||||
|  | ||||
|   | ||||
							
								
								
									
										99
									
								
								examples/confirm_answer.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										99
									
								
								examples/confirm_answer.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2015 Emmanuel Gil Peyrot | ||||
|     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 | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class AnswerConfirm(slixmpp.ClientXMPP): | ||||
|  | ||||
|     """ | ||||
|     A basic client demonstrating how to confirm or deny an HTTP request. | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, jid, password, trusted): | ||||
|         slixmpp.ClientXMPP.__init__(self, jid, password) | ||||
|  | ||||
|         self.add_event_handler("http_confirm", self.confirm) | ||||
|         self.add_event_handler("session_start", self.start) | ||||
|  | ||||
|     def start(self, *args): | ||||
|         self.make_presence().send() | ||||
|  | ||||
|     def prompt(self, stanza): | ||||
|         confirm = stanza['confirm'] | ||||
|         print('Received confirm request %s from %s to access %s using ' | ||||
|                  'method %s' % ( | ||||
|                      confirm['id'], stanza['from'], confirm['url'], | ||||
|                      confirm['method']) | ||||
|                 ) | ||||
|         result = input("Do you accept (y/N)? ") | ||||
|         return 'y' == result.lower() | ||||
|  | ||||
|     def confirm(self, stanza): | ||||
|         if self.prompt(stanza): | ||||
|             reply = stanza.reply() | ||||
|         else: | ||||
|             reply = stanza.reply() | ||||
|             reply.enable('error') | ||||
|             reply['error']['type'] = 'auth' | ||||
|             reply['error']['code'] = '401' | ||||
|             reply['error']['condition'] = 'not-authorized' | ||||
|         reply.append(stanza['confirm']) | ||||
|         reply.send() | ||||
|  | ||||
| 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.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") | ||||
|  | ||||
|     # Other options. | ||||
|     parser.add_argument("-t", "--trusted", nargs='*', | ||||
|                         help="List of trusted JIDs") | ||||
|  | ||||
|     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 = AnswerConfirm(args.jid, args.password, args.trusted) | ||||
|     xmpp.register_plugin('xep_0070') | ||||
|  | ||||
|     # Connect to the XMPP server and start processing XMPP stanzas. | ||||
|     xmpp.connect() | ||||
|     xmpp.process() | ||||
							
								
								
									
										124
									
								
								examples/confirm_ask.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										124
									
								
								examples/confirm_ask.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2015 Emmanuel Gil Peyrot | ||||
|     This file is part of Slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| import sys | ||||
|  | ||||
| import logging | ||||
| from getpass import getpass | ||||
| from argparse import ArgumentParser | ||||
|  | ||||
| import slixmpp | ||||
| from slixmpp.exceptions import XMPPError, IqError | ||||
| from slixmpp import asyncio | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class AskConfirm(slixmpp.ClientXMPP): | ||||
|  | ||||
|     """ | ||||
|     A basic client asking an entity if they confirm the access to an HTTP URL. | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, jid, password, recipient, id, url, method): | ||||
|         slixmpp.ClientXMPP.__init__(self, jid, password) | ||||
|  | ||||
|         self.recipient = recipient | ||||
|         self.id = id | ||||
|         self.url = url | ||||
|         self.method = method | ||||
|  | ||||
|         # Will be used to set the proper exit code. | ||||
|         self.confirmed = asyncio.Future() | ||||
|  | ||||
|         self.add_event_handler("session_start", self.start) | ||||
|         self.add_event_handler("message", self.start) | ||||
|         self.add_event_handler("http_confirm_message", self.confirm) | ||||
|  | ||||
|     def confirm(self, message): | ||||
|         print(message) | ||||
|         if message['confirm']['id'] == self.id: | ||||
|             if message['type'] == 'error': | ||||
|                 self.confirmed.set_result(False) | ||||
|             else: | ||||
|                 self.confirmed.set_result(True) | ||||
|  | ||||
|     async def start(self, event): | ||||
|         log.info('Sending confirm request %s to %s who wants to access %s using ' | ||||
|                  'method %s...' % (self.id, self.recipient, self.url, self.method)) | ||||
|         try: | ||||
|             confirmed = await self['xep_0070'].ask_confirm(self.recipient, | ||||
|                                                                 id=self.id, | ||||
|                                                                 url=self.url, | ||||
|                                                                 method=self.method, | ||||
|                                                                 message='Plz say yes or no for {method} {url} ({id}).') | ||||
|             if isinstance(confirmed, slixmpp.Message): | ||||
|                 confirmed = await self.confirmed | ||||
|             else: | ||||
|                 confirmed = True | ||||
|         except IqError: | ||||
|             confirmed = False | ||||
|         if confirmed: | ||||
|             print('Confirmed') | ||||
|         else: | ||||
|             print('Denied') | ||||
|         self.disconnect() | ||||
|  | ||||
|  | ||||
| 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.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") | ||||
|  | ||||
|     # Other options. | ||||
|     parser.add_argument("-r", "--recipient", required=True, | ||||
|                         help="Recipient JID") | ||||
|     parser.add_argument("-i", "--id", required=True, | ||||
|                         help="id TODO") | ||||
|     parser.add_argument("-u", "--url", required=True, | ||||
|                         help="URL the user tried to access") | ||||
|     parser.add_argument("-m", "--method", required=True, | ||||
|                         help="HTTP method used") | ||||
|  | ||||
|     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 = AskConfirm(args.jid, args.password, args.recipient, args.id, | ||||
|                       args.url, args.method) | ||||
|     xmpp.register_plugin('xep_0070') | ||||
|  | ||||
|     # Connect to the XMPP server and start processing XMPP stanzas. | ||||
|     xmpp.connect() | ||||
|     xmpp.process(forever=False) | ||||
|     sys.exit(0 if xmpp.confirmed else 1) | ||||
| @@ -50,7 +50,7 @@ class ActionBot(slixmpp.ClientXMPP): | ||||
|  | ||||
|         register_stanza_plugin(Iq, Action) | ||||
|  | ||||
|     def start(self, event): | ||||
|     async def start(self, event): | ||||
|         """ | ||||
|         Process the session_start event. | ||||
|  | ||||
| @@ -73,7 +73,7 @@ class ActionBot(slixmpp.ClientXMPP): | ||||
|         """ | ||||
|         self.event('custom_action', iq) | ||||
|  | ||||
|     def _handle_action_event(self, iq): | ||||
|     async def _handle_action_event(self, iq): | ||||
|         """ | ||||
|         Respond to the custom action event. | ||||
|         """ | ||||
| @@ -82,17 +82,20 @@ class ActionBot(slixmpp.ClientXMPP): | ||||
|  | ||||
|         if method == 'is_prime' and param == '2': | ||||
|             print("got message: %s" % iq) | ||||
|             iq.reply() | ||||
|             iq['action']['status'] = 'done' | ||||
|             iq.send() | ||||
|             rep = iq.reply() | ||||
|             rep['action']['status'] = 'done' | ||||
|             await rep.send() | ||||
|         elif method == 'bye': | ||||
|             print("got message: %s" % iq) | ||||
|             rep = iq.reply() | ||||
|             rep['action']['status'] = 'done' | ||||
|             await rep.send() | ||||
|             self.disconnect() | ||||
|         else: | ||||
|             print("got message: %s" % iq) | ||||
|             iq.reply() | ||||
|             iq['action']['status'] = 'error' | ||||
|             iq.send() | ||||
|             rep = iq.reply() | ||||
|             rep['action']['status'] = 'error' | ||||
|             await rep.send() | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     # Setup the command line arguments. | ||||
|   | ||||
| @@ -43,7 +43,7 @@ class ActionUserBot(slixmpp.ClientXMPP): | ||||
|  | ||||
|         register_stanza_plugin(Iq, Action) | ||||
|  | ||||
|     def start(self, event): | ||||
|     async def start(self, event): | ||||
|         """ | ||||
|         Process the session_start event. | ||||
|  | ||||
| @@ -57,11 +57,11 @@ class ActionUserBot(slixmpp.ClientXMPP): | ||||
|                      data. | ||||
|         """ | ||||
|         self.send_presence() | ||||
|         self.get_roster() | ||||
|         await self.get_roster() | ||||
|  | ||||
|         self.send_custom_iq() | ||||
|         await self.send_custom_iq() | ||||
|  | ||||
|     def send_custom_iq(self): | ||||
|     async def send_custom_iq(self): | ||||
|         """Create and send two custom actions. | ||||
|  | ||||
|         If the first action was successful, then send | ||||
| @@ -74,14 +74,14 @@ class ActionUserBot(slixmpp.ClientXMPP): | ||||
|         iq['action']['param'] = '2' | ||||
|  | ||||
|         try: | ||||
|             resp = iq.send() | ||||
|             resp = await 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) | ||||
|                 await iq2.send() | ||||
|  | ||||
|                 self.disconnect() | ||||
|         except XMPPError: | ||||
|   | ||||
| @@ -41,7 +41,7 @@ class Action(ElementBase): | ||||
|     #:     del action['status'] | ||||
|     #: | ||||
|     #: to set, get, or remove its values. | ||||
|     interfaces = set(('method', 'param', 'status')) | ||||
|     interfaces = {'method', 'param', 'status'} | ||||
|  | ||||
|     #: By default, values in the `interfaces` set are mapped to | ||||
|     #: attribute values. This can be changed such that an interface | ||||
|   | ||||
| @@ -15,7 +15,6 @@ from argparse import ArgumentParser | ||||
|  | ||||
| import slixmpp | ||||
| from slixmpp.exceptions import IqError, IqTimeout | ||||
| from slixmpp.xmlstream.asyncio import asyncio | ||||
|  | ||||
|  | ||||
| class Disco(slixmpp.ClientXMPP): | ||||
| @@ -54,8 +53,7 @@ class Disco(slixmpp.ClientXMPP): | ||||
|         # our roster. | ||||
|         self.add_event_handler("session_start", self.start) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def start(self, event): | ||||
|     async def start(self, event): | ||||
|         """ | ||||
|         Process the session_start event. | ||||
|  | ||||
| @@ -77,13 +75,13 @@ class Disco(slixmpp.ClientXMPP): | ||||
|         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, | ||||
|                 info = await 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, | ||||
|                 items = await 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.") | ||||
|   | ||||
| @@ -47,8 +47,7 @@ class AvatarDownloader(slixmpp.ClientXMPP): | ||||
|         self.roster_received.set() | ||||
|         self.presences_received.clear() | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def start(self, event): | ||||
|     async def start(self, event): | ||||
|         """ | ||||
|         Process the session_start event. | ||||
|  | ||||
| @@ -65,16 +64,15 @@ class AvatarDownloader(slixmpp.ClientXMPP): | ||||
|         self.get_roster(callback=self.roster_received_cb) | ||||
|  | ||||
|         print('Waiting for presence updates...\n') | ||||
|         yield from self.roster_received.wait() | ||||
|         await self.roster_received.wait() | ||||
|         print('Roster received') | ||||
|         yield from self.presences_received.wait() | ||||
|         await self.presences_received.wait() | ||||
|         self.disconnect() | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def on_vcard_avatar(self, pres): | ||||
|     async 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, | ||||
|             result = await self['xep_0054'].get_vcard(pres['from'].bare, cached=True, | ||||
|                                                            timeout=5) | ||||
|         except XMPPError: | ||||
|             print("Error retrieving avatar for %s" % pres['from']) | ||||
| @@ -89,14 +87,13 @@ class AvatarDownloader(slixmpp.ClientXMPP): | ||||
|         with open(filename, 'wb+') as img: | ||||
|             img.write(avatar['BINVAL']) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def on_avatar(self, msg): | ||||
|     async 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'], | ||||
|                     result = await self['xep_0084'].retrieve_avatar(msg['from'].bare, info['id'], | ||||
|                                                                          timeout=5) | ||||
|                 except XMPPError: | ||||
|                     print("Error retrieving avatar for %s" % msg['from']) | ||||
|   | ||||
| @@ -55,8 +55,8 @@ class GTalkBot(slixmpp.ClientXMPP): | ||||
|             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) | ||||
|             logging.error(err.message) | ||||
|             self.disconnect() | ||||
|  | ||||
|     def start(self, event): | ||||
|         """ | ||||
|   | ||||
| @@ -13,7 +13,7 @@ | ||||
|  | ||||
| from slixmpp import ClientXMPP | ||||
|  | ||||
| from optparse import OptionParser | ||||
| from argparse import ArgumentParser | ||||
| import logging | ||||
| import getpass | ||||
|  | ||||
| @@ -23,7 +23,7 @@ class HTTPOverXMPPClient(ClientXMPP): | ||||
|         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 | ||||
|             'session_start', self.session_start | ||||
|         ) | ||||
|         self.add_event_handler('http_request', self.http_request_received) | ||||
|         self.add_event_handler('http_response', self.http_response_received) | ||||
| @@ -58,40 +58,40 @@ if __name__ == '__main__': | ||||
|     # ./http_over_xmpp.py -J <jid> -P <pwd> -i <ip> -p <port> [-v] | ||||
|     # | ||||
|  | ||||
|     parser = OptionParser() | ||||
|     parser = ArgumentParser() | ||||
|  | ||||
|     # Output verbosity options. | ||||
|     parser.add_option( | ||||
|     parser.add_argument( | ||||
|         '-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') | ||||
|     parser.add_argument('-J', '--jid', dest='jid', help='JID') | ||||
|     parser.add_argument('-P', '--password', dest='password', help='Password') | ||||
|  | ||||
|     # XMPP server ip and port options. | ||||
|     parser.add_option( | ||||
|     parser.add_argument( | ||||
|         '-i', '--ipaddr', dest='ipaddr', | ||||
|         help='IP Address of the XMPP server', default=None | ||||
|     ) | ||||
|     parser.add_option( | ||||
|     parser.add_argument( | ||||
|         '-p', '--port', dest='port', | ||||
|         help='Port of the XMPP server', default=None | ||||
|     ) | ||||
|  | ||||
|     opts, args = parser.parse_args() | ||||
|     args = parser.parse_args() | ||||
|  | ||||
|     # Setup logging. | ||||
|     logging.basicConfig(level=opts.loglevel, | ||||
|     logging.basicConfig(level=args.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: ') | ||||
|     if args.jid is None: | ||||
|         args.jid = input('Username: ') | ||||
|     if args.password is None: | ||||
|         args.password = getpass.getpass('Password: ') | ||||
|  | ||||
|     xmpp = HTTPOverXMPPClient(opts.jid, opts.password) | ||||
|     xmpp = HTTPOverXMPPClient(args.jid, args.password) | ||||
|     xmpp.connect() | ||||
|     xmpp.process() | ||||
|  | ||||
|   | ||||
							
								
								
									
										96
									
								
								examples/http_upload.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										96
									
								
								examples/http_upload.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2018 Emmanuel Gil Peyrot | ||||
|     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 | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class HttpUpload(slixmpp.ClientXMPP): | ||||
|  | ||||
|     """ | ||||
|     A basic client asking an entity if they confirm the access to an HTTP URL. | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, jid, password, recipient, filename, domain=None): | ||||
|         slixmpp.ClientXMPP.__init__(self, jid, password) | ||||
|  | ||||
|         self.recipient = recipient | ||||
|         self.filename = filename | ||||
|         self.domain = domain | ||||
|  | ||||
|         self.add_event_handler("session_start", self.start) | ||||
|  | ||||
|     async def start(self, event): | ||||
|         log.info('Uploading file %s...', self.filename) | ||||
|         def timeout_callback(arg): | ||||
|             raise TimeoutError("could not send message in time") | ||||
|         url = await self['xep_0363'].upload_file( | ||||
|             self.filename, domain=self.domain, timeout=10, timeout_callback=timeout_callback) | ||||
|         log.info('Upload success!') | ||||
|  | ||||
|         log.info('Sending file to %s', self.recipient) | ||||
|         html = '<body xmlns="http://www.w3.org/1999/xhtml"><a href="%s">%s</a></body>' % (url, url) | ||||
|         self.send_message(self.recipient, url, mhtml=html) | ||||
|         self.disconnect() | ||||
|  | ||||
|  | ||||
| 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.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") | ||||
|  | ||||
|     # Other options. | ||||
|     parser.add_argument("-r", "--recipient", required=True, | ||||
|                         help="Recipient JID") | ||||
|     parser.add_argument("-f", "--file", required=True, | ||||
|                         help="File to send") | ||||
|     parser.add_argument("--domain", | ||||
|                         help="Domain to use for HTTP File Upload (leave out for your own server’s)") | ||||
|  | ||||
|     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 = HttpUpload(args.jid, args.password, args.recipient, args.file, args.domain) | ||||
|     xmpp.register_plugin('xep_0071') | ||||
|     xmpp.register_plugin('xep_0128') | ||||
|     xmpp.register_plugin('xep_0363') | ||||
|  | ||||
|     # Connect to the XMPP server and start processing XMPP stanzas. | ||||
|     xmpp.connect() | ||||
|     xmpp.process(forever=False) | ||||
| @@ -9,7 +9,6 @@ | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| import asyncio | ||||
| import logging | ||||
| from getpass import getpass | ||||
| from argparse import ArgumentParser | ||||
| @@ -39,8 +38,7 @@ class IBBSender(slixmpp.ClientXMPP): | ||||
|         # our roster. | ||||
|         self.add_event_handler("session_start", self.start) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def start(self, event): | ||||
|     async def start(self, event): | ||||
|         """ | ||||
|         Process the session_start event. | ||||
|  | ||||
| @@ -58,13 +56,13 @@ class IBBSender(slixmpp.ClientXMPP): | ||||
|  | ||||
|         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) | ||||
|             stream = await 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) | ||||
|             await stream.sendfile(self.file, timeout=10) | ||||
|  | ||||
|             # And finally close the stream. | ||||
|             yield from stream.close(timeout=10) | ||||
|             await stream.close(timeout=10) | ||||
|         except (IqError, IqTimeout): | ||||
|             print('File transfer errored') | ||||
|         else: | ||||
|   | ||||
							
								
								
									
										97
									
								
								examples/mam.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										97
									
								
								examples/mam.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2017 Mathieu Pasquet | ||||
|     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 | ||||
|  | ||||
| log = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class MAM(slixmpp.ClientXMPP): | ||||
|  | ||||
|     """ | ||||
|     A basic client fetching mam archive messages | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, jid, password, remote_jid, start): | ||||
|         slixmpp.ClientXMPP.__init__(self, jid, password) | ||||
|         self.remote_jid = remote_jid | ||||
|         self.start_date = start | ||||
|  | ||||
|         self.add_event_handler("session_start", self.start) | ||||
|  | ||||
|     async def start(self, *args): | ||||
|         """ | ||||
|         Fetch mam results for the specified JID. | ||||
|         Use RSM to paginate the results. | ||||
|         """ | ||||
|         results = self.plugin['xep_0313'].retrieve(jid=self.remote_jid, iterator=True, rsm={'max': 10}, start=self.start_date) | ||||
|         page = 1 | ||||
|         async for rsm in results: | ||||
|             print('Page %d' % page) | ||||
|             for msg in rsm['mam']['results']: | ||||
|                 forwarded = msg['mam_result']['forwarded'] | ||||
|                 timestamp = forwarded['delay']['stamp'] | ||||
|                 message = forwarded['stanza'] | ||||
|                 print('[%s] %s: %s' % (timestamp, message['from'], message['body'])) | ||||
|             page += 1 | ||||
|         self.disconnect() | ||||
|  | ||||
| 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.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") | ||||
|  | ||||
|     # Other options | ||||
|     parser.add_argument("-r", "--remote-jid", dest="remote_jid", | ||||
|                         help="Remote JID") | ||||
|     parser.add_argument("--start", help="Start date", default='2017-09-20T12:00:00Z') | ||||
|  | ||||
|     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.remote_jid is None: | ||||
|         args.remote_jid = input("Remote JID: ") | ||||
|     if args.start is None: | ||||
|         args.start = input("Start time: ") | ||||
|  | ||||
|     xmpp = MAM(args.jid, args.password, args.remote_jid, args.start) | ||||
|     xmpp.register_plugin('xep_0313') | ||||
|  | ||||
|     # Connect to the XMPP server and start processing XMPP stanzas. | ||||
|     xmpp.connect() | ||||
|     xmpp.process(forever=False) | ||||
							
								
								
									
										120
									
								
								examples/markup.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										120
									
								
								examples/markup.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| #!/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.plugins.xep_0394 import stanza as markup_stanza | ||||
|  | ||||
|  | ||||
| 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. | ||||
|         """ | ||||
|         body = msg['body'] | ||||
|         new_body = self['xep_0394'].to_plain_text(body, msg['markup']) | ||||
|         xhtml = self['xep_0394'].to_xhtml_im(body, msg['markup']) | ||||
|         print('Plain text:', new_body) | ||||
|         print('XHTML-IM:', xhtml['body']) | ||||
|         message = msg.reply() | ||||
|         message['body'] = new_body | ||||
|         message['html']['body'] = xhtml['body'] | ||||
|         self.send(message) | ||||
|  | ||||
|  | ||||
| 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_0199') # XMPP Ping | ||||
|     xmpp.register_plugin('xep_0394') # Message Markup | ||||
|  | ||||
|     # Connect to the XMPP server and start processing XMPP stanzas. | ||||
|     xmpp.connect() | ||||
|     xmpp.process() | ||||
| @@ -67,11 +67,11 @@ class MUCBot(slixmpp.ClientXMPP): | ||||
|         """ | ||||
|         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) | ||||
|         self.plugin['xep_0045'].join_muc(self.room, | ||||
|                                          self.nick, | ||||
|                                          # If a room password is needed, use: | ||||
|                                          # password=the_room_password, | ||||
|                                          wait=True) | ||||
|  | ||||
|     def muc_message(self, msg): | ||||
|         """ | ||||
|   | ||||
| @@ -13,7 +13,6 @@ import logging | ||||
| from getpass import getpass | ||||
| from argparse import ArgumentParser | ||||
| from slixmpp.exceptions import IqError, IqTimeout | ||||
| from slixmpp import asyncio | ||||
|  | ||||
| import slixmpp | ||||
|  | ||||
| @@ -38,8 +37,7 @@ class PingTest(slixmpp.ClientXMPP): | ||||
|         # our roster. | ||||
|         self.add_event_handler("session_start", self.start) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def start(self, event): | ||||
|     async def start(self, event): | ||||
|         """ | ||||
|         Process the session_start event. | ||||
|  | ||||
| @@ -56,7 +54,7 @@ class PingTest(slixmpp.ClientXMPP): | ||||
|         self.get_roster() | ||||
|  | ||||
|         try: | ||||
|             rtt = yield from self['xep_0199'].ping(self.pingjid, | ||||
|             rtt = await self['xep_0199'].ping(self.pingjid, | ||||
|                                                    timeout=10) | ||||
|             logging.info("Success! RTT: %s", rtt) | ||||
|         except IqError as e: | ||||
|   | ||||
| @@ -5,7 +5,6 @@ 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 | ||||
| @@ -15,13 +14,13 @@ class PubsubClient(slixmpp.ClientXMPP): | ||||
|  | ||||
|     def __init__(self, jid, password, server, | ||||
|                        node=None, action='nodes', data=''): | ||||
|         super(PubsubClient, self).__init__(jid, password) | ||||
|         super().__init__(jid, password) | ||||
|  | ||||
|         self.register_plugin('xep_0030') | ||||
|         self.register_plugin('xep_0059') | ||||
|         self.register_plugin('xep_0060') | ||||
|  | ||||
|         self.actions = ['nodes', 'create', 'delete', | ||||
|         self.actions = ['nodes', 'create', 'delete', 'get_configure', | ||||
|                         'publish', 'get', 'retract', | ||||
|                         'purge', 'subscribe', 'unsubscribe'] | ||||
|  | ||||
| @@ -32,80 +31,86 @@ class PubsubClient(slixmpp.ClientXMPP): | ||||
|  | ||||
|         self.add_event_handler('session_start', self.start) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def start(self, event): | ||||
|     async def start(self, event): | ||||
|         self.get_roster() | ||||
|         self.send_presence() | ||||
|  | ||||
|         try: | ||||
|             yield from getattr(self, self.action)() | ||||
|             await getattr(self, self.action)() | ||||
|         except: | ||||
|             logging.error('Could not execute: %s', self.action) | ||||
|             logging.exception('Could not execute %s:', self.action) | ||||
|         self.disconnect() | ||||
|  | ||||
|     def nodes(self): | ||||
|     async def nodes(self): | ||||
|         try: | ||||
|             result = yield from self['xep_0060'].get_nodes(self.pubsub_server, self.node) | ||||
|             result = await 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): | ||||
|     async def create(self): | ||||
|         try: | ||||
|             yield from self['xep_0060'].create_node(self.pubsub_server, self.node) | ||||
|             await 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): | ||||
|     async def delete(self): | ||||
|         try: | ||||
|             yield from self['xep_0060'].delete_node(self.pubsub_server, self.node) | ||||
|             await 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): | ||||
|     async def get_configure(self): | ||||
|         try: | ||||
|             configuration_form = await self['xep_0060'].get_node_config(self.pubsub_server, self.node) | ||||
|             logging.info('Configure form received from node %s: %s', self.node, configuration_form['pubsub_owner']['configure']['form']) | ||||
|         except XMPPError as error: | ||||
|             logging.error('Could not retrieve configure form from node %s: %s', self.node, error.format()) | ||||
|  | ||||
|     async 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) | ||||
|             result = await 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): | ||||
|     async def get(self): | ||||
|         try: | ||||
|             result = yield from self['xep_0060'].get_item(self.pubsub_server, self.node, self.data) | ||||
|             result = await 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): | ||||
|     async def retract(self): | ||||
|         try: | ||||
|             yield from self['xep_0060'].retract(self.pubsub_server, self.node, self.data) | ||||
|             await 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): | ||||
|     async def purge(self): | ||||
|         try: | ||||
|             yield from self['xep_0060'].purge(self.pubsub_server, self.node) | ||||
|             await 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): | ||||
|     async def subscribe(self): | ||||
|         try: | ||||
|             iq = yield from self['xep_0060'].subscribe(self.pubsub_server, self.node) | ||||
|             iq = await 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): | ||||
|     async def unsubscribe(self): | ||||
|         try: | ||||
|             yield from self['xep_0060'].unsubscribe(self.pubsub_server, self.node) | ||||
|             await 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()) | ||||
| @@ -118,7 +123,7 @@ if __name__ == '__main__': | ||||
|     parser = ArgumentParser() | ||||
|     parser.version = '%%prog 0.1' | ||||
|     parser.usage = "Usage: %%prog [options] <jid> " + \ | ||||
|                              'nodes|create|delete|purge|subscribe|unsubscribe|publish|retract|get' + \ | ||||
|                              'nodes|create|delete|get_configure|purge|subscribe|unsubscribe|publish|retract|get' + \ | ||||
|                              ' [<node> <data>]' | ||||
|  | ||||
|     parser.add_argument("-q","--quiet", help="set logging to ERROR", | ||||
| @@ -139,7 +144,7 @@ if __name__ == '__main__': | ||||
|                         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("action", choices=["nodes", "create", "delete", "get_configure", "purge", "subscribe", "unsubscribe", "publish", "retract", "get"]) | ||||
|     parser.add_argument("node", nargs='?') | ||||
|     parser.add_argument("data", nargs='?') | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,7 @@ from slixmpp.xmlstream.handler import Callback | ||||
| class PubsubEvents(slixmpp.ClientXMPP): | ||||
|  | ||||
|     def __init__(self, jid, password): | ||||
|         super(PubsubEvents, self).__init__(jid, password) | ||||
|         super().__init__(jid, password) | ||||
|  | ||||
|         self.register_plugin('xep_0030') | ||||
|         self.register_plugin('xep_0059') | ||||
|   | ||||
| @@ -66,7 +66,7 @@ class RegisterBot(slixmpp.ClientXMPP): | ||||
|         # We're only concerned about registering, so nothing more to do here. | ||||
|         self.disconnect() | ||||
|  | ||||
|     def register(self, iq): | ||||
|     async def register(self, iq): | ||||
|         """ | ||||
|         Fill out and submit a registration form. | ||||
|  | ||||
| @@ -90,7 +90,7 @@ class RegisterBot(slixmpp.ClientXMPP): | ||||
|         resp['register']['password'] = self.password | ||||
|  | ||||
|         try: | ||||
|             yield from resp.send() | ||||
|             await resp.send() | ||||
|             logging.info("Account created for %s!" % self.boundjid) | ||||
|         except IqError as e: | ||||
|             logging.error("Could not register account: %s" % | ||||
|   | ||||
| @@ -38,8 +38,7 @@ class RosterBrowser(slixmpp.ClientXMPP): | ||||
|         self.received = set() | ||||
|         self.presences_received = asyncio.Event() | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def start(self, event): | ||||
|     async def start(self, event): | ||||
|         """ | ||||
|         Process the session_start event. | ||||
|  | ||||
| @@ -57,7 +56,7 @@ class RosterBrowser(slixmpp.ClientXMPP): | ||||
|             future.set_result(None) | ||||
|         try: | ||||
|             self.get_roster(callback=callback) | ||||
|             yield from future | ||||
|             await future | ||||
|         except IqError as err: | ||||
|             print('Error: %s' % err.iq['error']['condition']) | ||||
|         except IqTimeout: | ||||
| @@ -66,7 +65,7 @@ class RosterBrowser(slixmpp.ClientXMPP): | ||||
|  | ||||
|  | ||||
|         print('Waiting for presence updates...\n') | ||||
|         yield from asyncio.sleep(10) | ||||
|         await asyncio.sleep(10) | ||||
|  | ||||
|         print('Roster for %s' % self.boundjid.bare) | ||||
|         groups = self.client_roster.groups() | ||||
|   | ||||
| @@ -9,7 +9,6 @@ | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| import asyncio | ||||
| import logging | ||||
| from getpass import getpass | ||||
| from argparse import ArgumentParser | ||||
|   | ||||
| @@ -9,7 +9,6 @@ | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| import asyncio | ||||
| import logging | ||||
| from getpass import getpass | ||||
| from argparse import ArgumentParser | ||||
| @@ -36,8 +35,7 @@ class S5BSender(slixmpp.ClientXMPP): | ||||
|         # and the XML streams are ready for use. | ||||
|         self.add_event_handler("session_start", self.start) | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def start(self, event): | ||||
|     async def start(self, event): | ||||
|         """ | ||||
|         Process the session_start event. | ||||
|  | ||||
| @@ -53,14 +51,14 @@ class S5BSender(slixmpp.ClientXMPP): | ||||
|  | ||||
|         try: | ||||
|             # Open the S5B stream in which to write to. | ||||
|             proxy = yield from self['xep_0065'].handshake(self.receiver) | ||||
|             proxy = await self['xep_0065'].handshake(self.receiver) | ||||
|  | ||||
|             # Send the entire file. | ||||
|             while True: | ||||
|                 data = self.file.read(1048576) | ||||
|                 if not data: | ||||
|                     break | ||||
|                 yield from proxy.write(data) | ||||
|                 await proxy.write(data) | ||||
|  | ||||
|             # And finally close the stream. | ||||
|             proxy.transport.write_eof() | ||||
|   | ||||
| @@ -18,7 +18,6 @@ from argparse import ArgumentParser | ||||
|  | ||||
| import slixmpp | ||||
| from slixmpp.exceptions import XMPPError | ||||
| from slixmpp import asyncio | ||||
|  | ||||
| class AvatarSetter(slixmpp.ClientXMPP): | ||||
|  | ||||
| @@ -33,8 +32,7 @@ class AvatarSetter(slixmpp.ClientXMPP): | ||||
|  | ||||
|         self.filepath = filepath | ||||
|  | ||||
|     @asyncio.coroutine | ||||
|     def start(self, event): | ||||
|     async def start(self, event): | ||||
|         """ | ||||
|         Process the session_start event. | ||||
|  | ||||
| @@ -68,20 +66,20 @@ class AvatarSetter(slixmpp.ClientXMPP): | ||||
|         used_xep84 = False | ||||
|  | ||||
|         print('Publish XEP-0084 avatar data') | ||||
|         result = yield from self['xep_0084'].publish_avatar(avatar) | ||||
|         result = await self['xep_0084'].publish_avatar(avatar) | ||||
|         if isinstance(result, XMPPError): | ||||
|             print('Could not publish XEP-0084 avatar') | ||||
|         else: | ||||
|             used_xep84 = True | ||||
|  | ||||
|         print('Update vCard with avatar') | ||||
|         result = yield from self['xep_0153'].set_avatar(avatar=avatar, mtype=avatar_type) | ||||
|         result = await self['xep_0153'].set_avatar(avatar=avatar, mtype=avatar_type) | ||||
|         if isinstance(result, XMPPError): | ||||
|             print('Could not set vCard avatar') | ||||
|  | ||||
|         if used_xep84: | ||||
|             print('Advertise XEP-0084 avatar metadata') | ||||
|             result = yield from self['xep_0084'].publish_avatar_metadata([ | ||||
|             result = await self['xep_0084'].publish_avatar_metadata([ | ||||
|                 {'id': avatar_id, | ||||
|                  'type': avatar_type, | ||||
|                  'bytes': avatar_bytes} | ||||
|   | ||||
| @@ -22,7 +22,7 @@ from slixmpp import ClientXMPP | ||||
| class LocationBot(ClientXMPP): | ||||
|  | ||||
|     def __init__(self, jid, password): | ||||
|         super(LocationBot, self).__init__(jid, password) | ||||
|         super().__init__(jid, password) | ||||
|  | ||||
|         self.add_event_handler('session_start', self.start) | ||||
|         self.add_event_handler('user_location_publish', | ||||
|   | ||||
| @@ -17,7 +17,7 @@ from slixmpp import ClientXMPP | ||||
| class TuneBot(ClientXMPP): | ||||
|  | ||||
|     def __init__(self, jid, password): | ||||
|         super(TuneBot, self).__init__(jid, password) | ||||
|         super().__init__(jid, password) | ||||
|  | ||||
|         # Check for the current song every 5 seconds. | ||||
|         self.schedule('Check Current Tune', 5, self._update_tune, repeat=True) | ||||
|   | ||||
							
								
								
									
										56
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								setup.py
									
									
									
									
									
								
							| @@ -7,26 +7,20 @@ | ||||
| # This software is licensed as described in the README.rst and LICENSE | ||||
| # file, which you should have received as part of this distribution. | ||||
|  | ||||
| import os | ||||
| from pathlib import Path | ||||
| from subprocess import call, DEVNULL, check_output, CalledProcessError | ||||
| from tempfile import TemporaryFile | ||||
| try: | ||||
|     from setuptools import setup | ||||
| except ImportError: | ||||
|     from distutils.core import setup | ||||
|  | ||||
| try: | ||||
|     from Cython.Build import cythonize | ||||
| except ImportError: | ||||
|     print('Cython not found, falling back to the slow stringprep module.') | ||||
|     ext_modules = None | ||||
| else: | ||||
|     ext_modules = cythonize('slixmpp/stringprep.pyx') | ||||
|  | ||||
| from run_tests import TestCommand | ||||
| from slixmpp.version import __version__ | ||||
|  | ||||
| VERSION = __version__ | ||||
| DESCRIPTION = ('Slixmpp is an elegant Python library for XMPP (aka Jabber, ' | ||||
|                'Google Talk, etc).') | ||||
| DESCRIPTION = ('Slixmpp is an elegant Python library for XMPP (aka Jabber).') | ||||
| with open('README.rst', encoding='utf8') as readme: | ||||
|     LONG_DESCRIPTION = readme.read() | ||||
|  | ||||
| @@ -34,12 +28,48 @@ CLASSIFIERS = [ | ||||
|     'Intended Audience :: Developers', | ||||
|     'License :: OSI Approved :: MIT License', | ||||
|     'Programming Language :: Python', | ||||
|     'Programming Language :: Python :: 3.4', | ||||
|     'Programming Language :: Python :: 3.7', | ||||
|     'Programming Language :: Python :: 3.8', | ||||
|     'Topic :: Internet :: XMPP', | ||||
|     'Topic :: Software Development :: Libraries :: Python Modules', | ||||
| ] | ||||
|  | ||||
| packages = [str(mod.parent) for mod in Path('slixmpp').rglob('__init__.py')] | ||||
|  | ||||
| def check_include(library_name, header): | ||||
|     command = [os.environ.get('PKG_CONFIG', 'pkg-config'), '--cflags', library_name] | ||||
|     try: | ||||
|         cflags = check_output(command).decode('utf-8').split() | ||||
|     except FileNotFoundError: | ||||
|         print('pkg-config not found.') | ||||
|         return False | ||||
|     except CalledProcessError: | ||||
|         # pkg-config already prints the missing libraries on stderr. | ||||
|         return False | ||||
|     command = [os.environ.get('CC', 'cc')] + cflags + ['-E', '-'] | ||||
|     with TemporaryFile('w+') as c_file: | ||||
|         c_file.write('#include <%s>' % header) | ||||
|         c_file.seek(0) | ||||
|         try: | ||||
|             return call(command, stdin=c_file, stdout=DEVNULL, stderr=DEVNULL) == 0 | ||||
|         except FileNotFoundError: | ||||
|             print('%s headers not found.' % library_name) | ||||
|             return False | ||||
|  | ||||
| HAS_PYTHON_HEADERS = check_include('python3', 'Python.h') | ||||
| HAS_STRINGPREP_HEADERS = check_include('libidn', 'stringprep.h') | ||||
|  | ||||
| ext_modules = None | ||||
| if HAS_PYTHON_HEADERS and HAS_STRINGPREP_HEADERS: | ||||
|     try: | ||||
|         from Cython.Build import cythonize | ||||
|     except ImportError: | ||||
|         print('Cython not found, falling back to the slow stringprep module.') | ||||
|     else: | ||||
|         ext_modules = cythonize('slixmpp/stringprep.pyx') | ||||
| else: | ||||
|     print('Falling back to the slow stringprep module.') | ||||
|  | ||||
| setup( | ||||
|     name="slixmpp", | ||||
|     version=VERSION, | ||||
| @@ -47,12 +77,12 @@ setup( | ||||
|     long_description=LONG_DESCRIPTION, | ||||
|     author='Florent Le Coz', | ||||
|     author_email='louiz@louiz.org', | ||||
|     url='https://dev.louiz.org/projects/slixmpp', | ||||
|     url='https://lab.louiz.org/poezio/slixmpp', | ||||
|     license='MIT', | ||||
|     platforms=['any'], | ||||
|     packages=packages, | ||||
|     ext_modules=ext_modules, | ||||
|     install_requires=['aiodns>=1.0', 'pyasn1', 'pyasn1_modules'], | ||||
|     install_requires=['aiodns>=1.0', 'pyasn1', 'pyasn1_modules', 'aiohttp'], | ||||
|     classifiers=CLASSIFIERS, | ||||
|     cmdclass={'test': TestCommand} | ||||
| ) | ||||
|   | ||||
| @@ -6,12 +6,13 @@ | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| import asyncio | ||||
| if hasattr(asyncio, 'sslproto'): # no ssl proto: very old asyncio = no need for this | ||||
|     asyncio.sslproto._is_sslproto_available=lambda: False | ||||
| import logging | ||||
| logging.getLogger(__name__).addHandler(logging.NullHandler()) | ||||
|  | ||||
| import asyncio | ||||
| # Required for python < 3.7 to use the old ssl implementation | ||||
| # and manage to do starttls as an unintended side effect | ||||
| asyncio.sslproto._is_sslproto_available = lambda: False | ||||
|  | ||||
| from slixmpp.stanza import Message, Presence, Iq | ||||
| from slixmpp.jid import JID, InvalidJID | ||||
|   | ||||
| @@ -12,8 +12,8 @@ | ||||
|     :license: MIT, see LICENSE for more details | ||||
| """ | ||||
|  | ||||
| import asyncio | ||||
| import logging | ||||
| import threading | ||||
|  | ||||
| from slixmpp import plugins, roster, stanza | ||||
| from slixmpp.api import APIRegistry | ||||
| @@ -21,7 +21,6 @@ from slixmpp.exceptions import IqError, IqTimeout | ||||
|  | ||||
| from slixmpp.stanza import Message, Presence, Iq, StreamError | ||||
| from slixmpp.stanza.roster import Roster | ||||
| from slixmpp.stanza.nick import Nick | ||||
|  | ||||
| from slixmpp.xmlstream import XMLStream, JID | ||||
| from slixmpp.xmlstream import ET, register_stanza_plugin | ||||
| @@ -70,7 +69,7 @@ class BaseXMPP(XMLStream): | ||||
|         #: redirections that will be followed before quitting. | ||||
|         self.max_redirects = 5 | ||||
|  | ||||
|         self.session_bind_event = threading.Event() | ||||
|         self.session_bind_event = asyncio.Event() | ||||
|  | ||||
|         #: A dictionary mapping plugin names to plugins. | ||||
|         self.plugin = PluginManager(self) | ||||
| @@ -105,12 +104,15 @@ class BaseXMPP(XMLStream): | ||||
|         #: :attr:`use_message_ids` to `True` will assign all outgoing | ||||
|         #: messages an ID. Some plugin features require enabling | ||||
|         #: this option. | ||||
|         self.use_message_ids = False | ||||
|         self.use_message_ids = True | ||||
|  | ||||
|         #: Presence updates may optionally be tagged with ID values. | ||||
|         #: Setting :attr:`use_message_ids` to `True` will assign all | ||||
|         #: outgoing messages an ID. | ||||
|         self.use_presence_ids = False | ||||
|         self.use_presence_ids = True | ||||
|  | ||||
|         #: XEP-0359 <origin-id/> tag that gets added to <message/> stanzas. | ||||
|         self.use_origin_id = True | ||||
|  | ||||
|         #: The API registry is a way to process callbacks based on | ||||
|         #: JID+node combinations. Each callback in the registry is | ||||
| @@ -194,7 +196,6 @@ class BaseXMPP(XMLStream): | ||||
|  | ||||
|         # Initialize a few default stanza plugins. | ||||
|         register_stanza_plugin(Iq, Roster) | ||||
|         register_stanza_plugin(Message, Nick) | ||||
|  | ||||
|     def start_stream_handler(self, xml): | ||||
|         """Save the stream ID once the streams have been established. | ||||
| @@ -687,7 +688,6 @@ class BaseXMPP(XMLStream): | ||||
|             self.address = (host, port) | ||||
|             self.default_domain = host | ||||
|             self.dns_records = None | ||||
|             self.reconnect_delay = None | ||||
|             self.reconnect() | ||||
|  | ||||
|     def _handle_message(self, msg): | ||||
| @@ -753,6 +753,9 @@ class BaseXMPP(XMLStream): | ||||
|  | ||||
|         Update the roster with presence information. | ||||
|         """ | ||||
|         if self.roster[presence['from']].ignore_updates: | ||||
|             return | ||||
|  | ||||
|         if not self.is_component and not presence['to'].bare: | ||||
|             presence['to'] = self.boundjid | ||||
|  | ||||
|   | ||||
| @@ -12,14 +12,16 @@ | ||||
|     :license: MIT, see LICENSE for more details | ||||
| """ | ||||
|  | ||||
| import asyncio | ||||
| import logging | ||||
|  | ||||
| from slixmpp.jid import JID | ||||
| from slixmpp.stanza import StreamFeatures | ||||
| from slixmpp.basexmpp import BaseXMPP | ||||
| from slixmpp.exceptions import XMPPError | ||||
| from slixmpp.xmlstream import XMLStream | ||||
| from slixmpp.xmlstream.matcher import StanzaPath, MatchXPath | ||||
| from slixmpp.xmlstream.handler import Callback | ||||
| from slixmpp.xmlstream.handler import Callback, CoroutineCallback | ||||
|  | ||||
| # Flag indicating if DNS SRV records are available for use. | ||||
| try: | ||||
| @@ -104,13 +106,24 @@ class ClientXMPP(BaseXMPP): | ||||
|         self.register_stanza(StreamFeatures) | ||||
|  | ||||
|         self.register_handler( | ||||
|                 Callback('Stream Features', | ||||
|                          MatchXPath('{%s}features' % self.stream_ns), | ||||
|                          self._handle_stream_features)) | ||||
|                 CoroutineCallback('Stream Features', | ||||
|                      MatchXPath('{%s}features' % self.stream_ns), | ||||
|                      self._handle_stream_features)) | ||||
|         def roster_push_filter(iq): | ||||
|             from_ = iq['from'] | ||||
|             if from_ and from_ != JID('') and from_ != self.boundjid.bare: | ||||
|                 reply = iq.reply() | ||||
|                 reply['type'] = 'error' | ||||
|                 reply['error']['type'] = 'cancel' | ||||
|                 reply['error']['code'] = 503 | ||||
|                 reply['error']['condition'] = 'service-unavailable' | ||||
|                 reply.send() | ||||
|                 return | ||||
|             self.event('roster_update', iq) | ||||
|         self.register_handler( | ||||
|                 Callback('Roster Update', | ||||
|                          StanzaPath('iq@type=set/roster'), | ||||
|                          lambda iq: self.event('roster_update', iq))) | ||||
|                          roster_push_filter)) | ||||
|  | ||||
|         # Setup default stream features | ||||
|         self.register_plugin('feature_starttls') | ||||
| @@ -140,8 +153,11 @@ class ClientXMPP(BaseXMPP): | ||||
|         will be used. | ||||
|  | ||||
|         :param address: A tuple containing the server's host and port. | ||||
|         :param use_tls: Indicates if TLS should be used for the | ||||
|                         connection. Defaults to ``True``. | ||||
|         :param force_starttls: Indicates that negotiation should be aborted | ||||
|                                if the server does not advertise support for | ||||
|                                STARTTLS. Defaults to ``True``. | ||||
|         :param disable_starttls: Disables TLS for the connection. | ||||
|                                  Defaults to ``False``. | ||||
|         :param use_ssl: Indicates if the older SSL connection method | ||||
|                         should be used. Defaults to ``False``. | ||||
|         """ | ||||
| @@ -239,7 +255,7 @@ class ClientXMPP(BaseXMPP): | ||||
|                 orig_cb(resp) | ||||
|             callback = wrapped | ||||
|  | ||||
|         iq.send(callback, timeout, timeout_callback) | ||||
|         return iq.send(callback, timeout, timeout_callback) | ||||
|  | ||||
|     def _reset_connection_state(self, event=None): | ||||
|         #TODO: Use stream state here | ||||
| @@ -249,7 +265,7 @@ class ClientXMPP(BaseXMPP): | ||||
|         self.bindfail = False | ||||
|         self.features = set() | ||||
|  | ||||
|     def _handle_stream_features(self, features): | ||||
|     async def _handle_stream_features(self, features): | ||||
|         """Process the received stream features. | ||||
|  | ||||
|         :param features: The features stanza. | ||||
| @@ -257,7 +273,11 @@ class ClientXMPP(BaseXMPP): | ||||
|         for order, name in self._stream_feature_order: | ||||
|             if name in features['features']: | ||||
|                 handler, restart = self._stream_feature_handlers[name] | ||||
|                 if handler(features) and restart: | ||||
|                 if asyncio.iscoroutinefunction(handler): | ||||
|                     result = await handler(features) | ||||
|                 else: | ||||
|                     result = handler(features) | ||||
|                 if result and restart: | ||||
|                     # Don't continue if the feature requires | ||||
|                     # restarting the XML stream. | ||||
|                     return True | ||||
|   | ||||
| @@ -77,7 +77,7 @@ class IqTimeout(XMPPError): | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, iq): | ||||
|         super(IqTimeout, self).__init__( | ||||
|         super().__init__( | ||||
|                 condition='remote-server-timeout', | ||||
|                 etype='cancel') | ||||
|  | ||||
| @@ -94,7 +94,7 @@ class IqError(XMPPError): | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, iq): | ||||
|         super(IqError, self).__init__( | ||||
|         super().__init__( | ||||
|                 condition=iq['error']['condition'], | ||||
|                 text=iq['error']['text'], | ||||
|                 etype=iq['error']['type']) | ||||
|   | ||||
| @@ -13,7 +13,3 @@ from slixmpp.features.feature_bind.stanza import Bind | ||||
|  | ||||
|  | ||||
| register_plugin(FeatureBind) | ||||
|  | ||||
|  | ||||
| # Retain some backwards compatibility | ||||
| feature_bind = FeatureBind | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| import asyncio | ||||
| import logging | ||||
|  | ||||
| from slixmpp.jid import JID | ||||
| @@ -34,7 +35,7 @@ class FeatureBind(BasePlugin): | ||||
|         register_stanza_plugin(Iq, stanza.Bind) | ||||
|         register_stanza_plugin(StreamFeatures, stanza.Bind) | ||||
|  | ||||
|     def _handle_bind_resource(self, features): | ||||
|     async def _handle_bind_resource(self, features): | ||||
|         """ | ||||
|         Handle requesting a specific resource. | ||||
|  | ||||
| @@ -49,7 +50,7 @@ class FeatureBind(BasePlugin): | ||||
|         if self.xmpp.requested_jid.resource: | ||||
|             iq['bind']['resource'] = self.xmpp.requested_jid.resource | ||||
|  | ||||
|         iq.send(callback=self._on_bind_response) | ||||
|         await iq.send(callback=self._on_bind_response) | ||||
|  | ||||
|     def _on_bind_response(self, response): | ||||
|         self.xmpp.boundjid = JID(response['bind']['jid']) | ||||
|   | ||||
| @@ -16,6 +16,6 @@ class Bind(ElementBase): | ||||
|  | ||||
|     name = 'bind' | ||||
|     namespace = 'urn:ietf:params:xml:ns:xmpp-bind' | ||||
|     interfaces = set(('resource', 'jid')) | ||||
|     interfaces = {'resource', 'jid'} | ||||
|     sub_interfaces = interfaces | ||||
|     plugin_attrib = 'bind' | ||||
|   | ||||
| @@ -16,7 +16,3 @@ from slixmpp.features.feature_mechanisms.stanza import Failure | ||||
|  | ||||
|  | ||||
| register_plugin(FeatureMechanisms) | ||||
|  | ||||
|  | ||||
| # Retain some backwards compatibility | ||||
| feature_mechanisms = FeatureMechanisms | ||||
|   | ||||
| @@ -49,7 +49,7 @@ class FeatureMechanisms(BasePlugin): | ||||
|         if self.security_callback is None: | ||||
|             self.security_callback = self._default_security | ||||
|  | ||||
|         creds = self.sasl_callback(set(['username']), set()) | ||||
|         creds = self.sasl_callback({'username'}, set()) | ||||
|         if not self.use_mech and not creds['username']: | ||||
|             self.use_mech = 'ANONYMOUS' | ||||
|  | ||||
| @@ -97,12 +97,9 @@ class FeatureMechanisms(BasePlugin): | ||||
|                 jid = self.xmpp.requested_jid.bare | ||||
|                 result[value] = creds.get('email', jid) | ||||
|             elif value == 'channel_binding': | ||||
|                 if hasattr(self.xmpp.socket, 'get_channel_binding'): | ||||
|                 if isinstance(self.xmpp.socket, (ssl.SSLSocket, ssl.SSLObject)): | ||||
|                     result[value] = self.xmpp.socket.get_channel_binding() | ||||
|                 else: | ||||
|                     log.debug("Channel binding not supported.") | ||||
|                     log.debug("Use Python 3.3+ for channel binding and " + \ | ||||
|                               "SCRAM-SHA-1-PLUS support") | ||||
|                     result[value] = None | ||||
|             elif value == 'host': | ||||
|                 result[value] = creds.get('host', self.xmpp.requested_jid.domain) | ||||
| @@ -122,7 +119,7 @@ class FeatureMechanisms(BasePlugin): | ||||
|             if value == 'encrypted': | ||||
|                 if 'starttls' in self.xmpp.features: | ||||
|                     result[value] = True | ||||
|                 elif isinstance(self.xmpp.socket, ssl.SSLSocket): | ||||
|                 elif isinstance(self.xmpp.socket, (ssl.SSLSocket, ssl.SSLObject)): | ||||
|                     result[value] = True | ||||
|                 else: | ||||
|                     result[value] = False | ||||
|   | ||||
| @@ -19,12 +19,12 @@ class Auth(StanzaBase): | ||||
|  | ||||
|     name = 'auth' | ||||
|     namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' | ||||
|     interfaces = set(('mechanism', 'value')) | ||||
|     interfaces = {'mechanism', 'value'} | ||||
|     plugin_attrib = name | ||||
|  | ||||
|     #: Some SASL mechs require sending values as is, | ||||
|     #: without converting base64. | ||||
|     plain_mechs = set(['X-MESSENGER-OAUTH2']) | ||||
|     plain_mechs = {'X-MESSENGER-OAUTH2'} | ||||
|  | ||||
|     def setup(self, xml): | ||||
|         StanzaBase.setup(self, xml) | ||||
|   | ||||
| @@ -19,7 +19,7 @@ class Challenge(StanzaBase): | ||||
|  | ||||
|     name = 'challenge' | ||||
|     namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' | ||||
|     interfaces = set(('value',)) | ||||
|     interfaces = {'value'} | ||||
|     plugin_attrib = name | ||||
|  | ||||
|     def setup(self, xml): | ||||
|   | ||||
| @@ -16,13 +16,14 @@ class Failure(StanzaBase): | ||||
|  | ||||
|     name = 'failure' | ||||
|     namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' | ||||
|     interfaces = set(('condition', 'text')) | ||||
|     interfaces = {'condition', 'text'} | ||||
|     plugin_attrib = name | ||||
|     sub_interfaces = set(('text',)) | ||||
|     conditions = set(('aborted', 'account-disabled', 'credentials-expired', | ||||
|         'encryption-required', 'incorrect-encoding', 'invalid-authzid', | ||||
|         'invalid-mechanism', 'malformed-request', 'mechansism-too-weak', | ||||
|         'not-authorized', 'temporary-auth-failure')) | ||||
|     sub_interfaces = {'text'} | ||||
|     conditions = {'aborted', 'account-disabled', 'credentials-expired', | ||||
|                   'encryption-required', 'incorrect-encoding', | ||||
|                   'invalid-authzid', 'invalid-mechanism', 'malformed-request', | ||||
|                   'mechansism-too-weak', 'not-authorized', | ||||
|                   'temporary-auth-failure'} | ||||
|  | ||||
|     def setup(self, xml=None): | ||||
|         """ | ||||
|   | ||||
| @@ -16,7 +16,7 @@ class Mechanisms(ElementBase): | ||||
|  | ||||
|     name = 'mechanisms' | ||||
|     namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' | ||||
|     interfaces = set(('mechanisms', 'required')) | ||||
|     interfaces = {'mechanisms', 'required'} | ||||
|     plugin_attrib = name | ||||
|     is_extension = True | ||||
|  | ||||
| @@ -29,7 +29,7 @@ class Mechanisms(ElementBase): | ||||
|         """ | ||||
|         """ | ||||
|         results = [] | ||||
|         mechs = self.findall('{%s}mechanism' % self.namespace) | ||||
|         mechs = self.xml.findall('{%s}mechanism' % self.namespace) | ||||
|         if mechs: | ||||
|             for mech in mechs: | ||||
|                 results.append(mech.text) | ||||
| @@ -47,7 +47,7 @@ class Mechanisms(ElementBase): | ||||
|     def del_mechanisms(self): | ||||
|         """ | ||||
|         """ | ||||
|         mechs = self.findall('{%s}mechanism' % self.namespace) | ||||
|         mechs = self.xml.findall('{%s}mechanism' % self.namespace) | ||||
|         if mechs: | ||||
|             for mech in mechs: | ||||
|                 self.xml.remove(mech) | ||||
|   | ||||
| @@ -19,7 +19,7 @@ class Response(StanzaBase): | ||||
|  | ||||
|     name = 'response' | ||||
|     namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' | ||||
|     interfaces = set(('value',)) | ||||
|     interfaces = {'value'} | ||||
|     plugin_attrib = name | ||||
|  | ||||
|     def setup(self, xml): | ||||
|   | ||||
| @@ -18,7 +18,7 @@ class Success(StanzaBase): | ||||
|  | ||||
|     name = 'success' | ||||
|     namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' | ||||
|     interfaces = set(['value']) | ||||
|     interfaces = {'value'} | ||||
|     plugin_attrib = name | ||||
|  | ||||
|     def setup(self, xml): | ||||
|   | ||||
| @@ -13,7 +13,3 @@ from slixmpp.features.feature_rosterver.stanza import RosterVer | ||||
|  | ||||
|  | ||||
| register_plugin(FeatureRosterVer) | ||||
|  | ||||
|  | ||||
| # Retain some backwards compatibility | ||||
| feature_rosterver = FeatureRosterVer | ||||
|   | ||||
| @@ -13,7 +13,3 @@ from slixmpp.features.feature_session.stanza import Session | ||||
|  | ||||
|  | ||||
| register_plugin(FeatureSession) | ||||
|  | ||||
|  | ||||
| # Retain some backwards compatibility | ||||
| feature_session = FeatureSession | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| import asyncio | ||||
| import logging | ||||
|  | ||||
| from slixmpp.stanza import Iq, StreamFeatures | ||||
| @@ -34,17 +35,22 @@ class FeatureSession(BasePlugin): | ||||
|         register_stanza_plugin(Iq, stanza.Session) | ||||
|         register_stanza_plugin(StreamFeatures, stanza.Session) | ||||
|  | ||||
|     def _handle_start_session(self, features): | ||||
|     async def _handle_start_session(self, features): | ||||
|         """ | ||||
|         Handle the start of the session. | ||||
|  | ||||
|         Arguments: | ||||
|             feature -- The stream features element. | ||||
|         """ | ||||
|         if features['session']['optional']: | ||||
|             self.xmpp.sessionstarted = True | ||||
|             self.xmpp.event('session_start') | ||||
|             return | ||||
|  | ||||
|         iq = self.xmpp.Iq() | ||||
|         iq['type'] = 'set' | ||||
|         iq.enable('session') | ||||
|         iq.send(callback=self._on_start_session_response) | ||||
|         await iq.send(callback=self._on_start_session_response) | ||||
|  | ||||
|     def _on_start_session_response(self, response): | ||||
|         self.xmpp.features.add('session') | ||||
|   | ||||
| @@ -6,7 +6,7 @@ | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| from slixmpp.xmlstream import ElementBase | ||||
| from slixmpp.xmlstream import ElementBase, ET | ||||
|  | ||||
|  | ||||
| class Session(ElementBase): | ||||
| @@ -16,5 +16,19 @@ class Session(ElementBase): | ||||
|  | ||||
|     name = 'session' | ||||
|     namespace = 'urn:ietf:params:xml:ns:xmpp-session' | ||||
|     interfaces = set() | ||||
|     interfaces = {'optional'} | ||||
|     plugin_attrib = 'session' | ||||
|  | ||||
|     def get_optional(self): | ||||
|         return self.xml.find('{%s}optional' % self.namespace) is not None | ||||
|  | ||||
|     def set_optional(self, value): | ||||
|         if value: | ||||
|             optional = ET.Element('{%s}optional' % self.namespace) | ||||
|             self.xml.append(optional) | ||||
|         else: | ||||
|             self.del_optional() | ||||
|  | ||||
|     def del_optional(self): | ||||
|         optional = self.xml.find('{%s}optional' % self.namespace) | ||||
|         self.xml.remove(optional) | ||||
|   | ||||
| @@ -13,7 +13,3 @@ from slixmpp.features.feature_starttls.stanza import * | ||||
|  | ||||
|  | ||||
| register_plugin(FeatureSTARTTLS) | ||||
|  | ||||
|  | ||||
| # Retain some backwards compatibility | ||||
| feature_starttls = FeatureSTARTTLS | ||||
|   | ||||
| @@ -16,7 +16,7 @@ class STARTTLS(ElementBase): | ||||
|  | ||||
|     name = 'starttls' | ||||
|     namespace = 'urn:ietf:params:xml:ns:xmpp-tls' | ||||
|     interfaces = set(('required',)) | ||||
|     interfaces = {'required'} | ||||
|     plugin_attrib = name | ||||
|  | ||||
|     def get_required(self): | ||||
|   | ||||
| @@ -12,7 +12,7 @@ from slixmpp.stanza import StreamFeatures | ||||
| from slixmpp.xmlstream import register_stanza_plugin | ||||
| from slixmpp.plugins import BasePlugin | ||||
| from slixmpp.xmlstream.matcher import MatchXPath | ||||
| from slixmpp.xmlstream.handler import Callback | ||||
| from slixmpp.xmlstream.handler import CoroutineCallback | ||||
| from slixmpp.features.feature_starttls import stanza | ||||
|  | ||||
|  | ||||
| @@ -28,7 +28,7 @@ class FeatureSTARTTLS(BasePlugin): | ||||
|  | ||||
|     def plugin_init(self): | ||||
|         self.xmpp.register_handler( | ||||
|                 Callback('STARTTLS Proceed', | ||||
|                 CoroutineCallback('STARTTLS Proceed', | ||||
|                         MatchXPath(stanza.Proceed.tag_name()), | ||||
|                         self._handle_starttls_proceed, | ||||
|                         instream=True)) | ||||
| @@ -58,8 +58,8 @@ class FeatureSTARTTLS(BasePlugin): | ||||
|             self.xmpp.send(features['starttls']) | ||||
|             return True | ||||
|  | ||||
|     def _handle_starttls_proceed(self, proceed): | ||||
|     async def _handle_starttls_proceed(self, proceed): | ||||
|         """Restart the XML stream when TLS is accepted.""" | ||||
|         log.debug("Starting TLS") | ||||
|         if self.xmpp.start_tls(): | ||||
|         if await self.xmpp.start_tls(): | ||||
|             self.xmpp.features.add('starttls') | ||||
|   | ||||
							
								
								
									
										158
									
								
								slixmpp/jid.py
									
									
									
									
									
								
							
							
						
						
									
										158
									
								
								slixmpp/jid.py
									
									
									
									
									
								
							| @@ -16,6 +16,7 @@ import socket | ||||
|  | ||||
| from copy import deepcopy | ||||
| from functools import lru_cache | ||||
| from typing import Optional | ||||
|  | ||||
| from slixmpp.stringprep import nodeprep, resourceprep, idna, StringprepError | ||||
|  | ||||
| @@ -71,7 +72,7 @@ def _parse_jid(data): | ||||
|     return node, domain, resource | ||||
|  | ||||
|  | ||||
| def _validate_node(node): | ||||
| def _validate_node(node: Optional[str]): | ||||
|     """Validate the local, or username, portion of a JID. | ||||
|  | ||||
|     :raises InvalidJID: | ||||
| @@ -79,7 +80,7 @@ def _validate_node(node): | ||||
|     :returns: The local portion of a JID, as validated by nodeprep. | ||||
|     """ | ||||
|     if node is None: | ||||
|         return None | ||||
|         return '' | ||||
|  | ||||
|     try: | ||||
|         node = nodeprep(node) | ||||
| @@ -93,7 +94,7 @@ def _validate_node(node): | ||||
|     return node | ||||
|  | ||||
|  | ||||
| def _validate_domain(domain): | ||||
| def _validate_domain(domain: str): | ||||
|     """Validate the domain portion of a JID. | ||||
|  | ||||
|     IP literal addresses are left as-is, if valid. Domain names | ||||
| @@ -152,7 +153,7 @@ def _validate_domain(domain): | ||||
|     return domain | ||||
|  | ||||
|  | ||||
| def _validate_resource(resource): | ||||
| def _validate_resource(resource: Optional[str]): | ||||
|     """Validate the resource portion of a JID. | ||||
|  | ||||
|     :raises InvalidJID: | ||||
| @@ -160,7 +161,7 @@ def _validate_resource(resource): | ||||
|     :returns: The local portion of a JID, as validated by resourceprep. | ||||
|     """ | ||||
|     if resource is None: | ||||
|         return None | ||||
|         return '' | ||||
|  | ||||
|     try: | ||||
|         resource = resourceprep(resource) | ||||
| @@ -174,7 +175,7 @@ def _validate_resource(resource): | ||||
|     return resource | ||||
|  | ||||
|  | ||||
| def _unescape_node(node): | ||||
| def _unescape_node(node: str): | ||||
|     """Unescape a local portion of a JID. | ||||
|  | ||||
|     .. note:: | ||||
| @@ -199,7 +200,11 @@ def _unescape_node(node): | ||||
|     return ''.join(unescaped) | ||||
|  | ||||
|  | ||||
| def _format_jid(local=None, domain=None, resource=None): | ||||
| def _format_jid( | ||||
|         local: Optional[str] = None, | ||||
|         domain: Optional[str] = None, | ||||
|         resource: Optional[str] = None, | ||||
|     ): | ||||
|     """Format the given JID components into a full or bare JID. | ||||
|  | ||||
|     :param string local: Optional. The local portion of the JID. | ||||
| @@ -208,16 +213,15 @@ def _format_jid(local=None, domain=None, resource=None): | ||||
|  | ||||
|     :return: A full or bare JID string. | ||||
|     """ | ||||
|     result = [] | ||||
|     if domain is None: | ||||
|         return '' | ||||
|     if local is not None: | ||||
|         result.append(local) | ||||
|         result.append('@') | ||||
|     if domain is not None: | ||||
|         result.append(domain) | ||||
|         result = local + '@' + domain | ||||
|     else: | ||||
|         result = domain | ||||
|     if resource is not None: | ||||
|         result.append('/') | ||||
|         result.append(resource) | ||||
|     return ''.join(result) | ||||
|         result += '/' + resource | ||||
|     return result | ||||
|  | ||||
|  | ||||
| class InvalidJID(ValueError): | ||||
| @@ -238,12 +242,17 @@ class UnescapedJID: | ||||
|  | ||||
|     __slots__ = ('_node', '_domain', '_resource') | ||||
|  | ||||
|     def __init__(self, node, domain, resource): | ||||
|     def __init__( | ||||
|             self, | ||||
|             node: Optional[str], | ||||
|             domain: Optional[str], | ||||
|             resource: Optional[str], | ||||
|         ): | ||||
|         self._node = node | ||||
|         self._domain = domain | ||||
|         self._resource = resource | ||||
|  | ||||
|     def __getattribute__(self, name): | ||||
|     def __getattribute__(self, name: str): | ||||
|         """Retrieve the given JID component. | ||||
|  | ||||
|         :param name: one of: user, server, domain, resource, | ||||
| @@ -300,19 +309,23 @@ class JID: | ||||
|     :raises InvalidJID: | ||||
|     """ | ||||
|  | ||||
|     __slots__ = ('_node', '_domain', '_resource') | ||||
|     __slots__ = ('_node', '_domain', '_resource', '_bare', '_full') | ||||
|  | ||||
|     def __init__(self, jid=None): | ||||
|     def __init__(self, jid: Optional[str] = None): | ||||
|         if not jid: | ||||
|             self._node = None | ||||
|             self._domain = None | ||||
|             self._resource = None | ||||
|             self._node = '' | ||||
|             self._domain = '' | ||||
|             self._resource = '' | ||||
|             self._bare = '' | ||||
|             self._full = '' | ||||
|             return | ||||
|         elif not isinstance(jid, JID): | ||||
|             self._node, self._domain, self._resource = _parse_jid(jid) | ||||
|         else: | ||||
|             self._node = jid._node | ||||
|             self._domain = jid._domain | ||||
|             self._resource = jid._resource | ||||
|         self._update_bare_full() | ||||
|  | ||||
|     def unescape(self): | ||||
|         """Return an unescaped JID object. | ||||
| @@ -329,104 +342,80 @@ class JID: | ||||
|                             self._domain, | ||||
|                             self._resource) | ||||
|  | ||||
|     def _update_bare_full(self): | ||||
|         """Format the given JID into a bare and a full JID. | ||||
|         """ | ||||
|         self._bare = (self._node + '@' + self._domain | ||||
|                       if self._node | ||||
|                       else self._domain) | ||||
|         self._full = (self._bare + '/' + self._resource | ||||
|                       if self._resource | ||||
|                       else self._bare) | ||||
|  | ||||
|     @property | ||||
|     def node(self): | ||||
|         return self._node or '' | ||||
|  | ||||
|     @property | ||||
|     def user(self): | ||||
|         return self._node or '' | ||||
|  | ||||
|     @property | ||||
|     def local(self): | ||||
|         return self._node or '' | ||||
|  | ||||
|     @property | ||||
|     def username(self): | ||||
|         return self._node or '' | ||||
|         return self._node | ||||
|  | ||||
|     @property | ||||
|     def domain(self): | ||||
|         return self._domain or '' | ||||
|  | ||||
|     @property | ||||
|     def server(self): | ||||
|         return self._domain or '' | ||||
|  | ||||
|     @property | ||||
|     def host(self): | ||||
|         return self._domain or '' | ||||
|         return self._domain | ||||
|  | ||||
|     @property | ||||
|     def resource(self): | ||||
|         return self._resource or '' | ||||
|         return self._resource | ||||
|  | ||||
|     @property | ||||
|     def bare(self): | ||||
|         return _format_jid(self._node, self._domain) | ||||
|         return self._bare | ||||
|  | ||||
|     @property | ||||
|     def full(self): | ||||
|         return _format_jid(self._node, self._domain, self._resource) | ||||
|  | ||||
|     @property | ||||
|     def jid(self): | ||||
|         return _format_jid(self._node, self._domain, self._resource) | ||||
|         return self._full | ||||
|  | ||||
|     @node.setter | ||||
|     def node(self, value): | ||||
|         self._node = _validate_node(value) | ||||
|  | ||||
|     @user.setter | ||||
|     def user(self, value): | ||||
|         self._node = _validate_node(value) | ||||
|  | ||||
|     @local.setter | ||||
|     def local(self, value): | ||||
|         self._node = _validate_node(value) | ||||
|  | ||||
|     @username.setter | ||||
|     def username(self, value): | ||||
|     def node(self, value: str): | ||||
|         self._node = _validate_node(value) | ||||
|         self._update_bare_full() | ||||
|  | ||||
|     @domain.setter | ||||
|     def domain(self, value): | ||||
|         self._domain = _validate_domain(value) | ||||
|  | ||||
|     @server.setter | ||||
|     def server(self, value): | ||||
|         self._domain = _validate_domain(value) | ||||
|  | ||||
|     @host.setter | ||||
|     def host(self, value): | ||||
|     def domain(self, value: str): | ||||
|         self._domain = _validate_domain(value) | ||||
|         self._update_bare_full() | ||||
|  | ||||
|     @bare.setter | ||||
|     def bare(self, value): | ||||
|     def bare(self, value: str): | ||||
|         node, domain, resource = _parse_jid(value) | ||||
|         assert not resource | ||||
|         self._node = node | ||||
|         self._domain = domain | ||||
|         self._update_bare_full() | ||||
|  | ||||
|     @resource.setter | ||||
|     def resource(self, value): | ||||
|     def resource(self, value: str): | ||||
|         self._resource = _validate_resource(value) | ||||
|         self._update_bare_full() | ||||
|  | ||||
|     @full.setter | ||||
|     def full(self, value): | ||||
|     def full(self, value: str): | ||||
|         self._node, self._domain, self._resource = _parse_jid(value) | ||||
|         self._update_bare_full() | ||||
|  | ||||
|     @jid.setter | ||||
|     def jid(self, value): | ||||
|         self._node, self._domain, self._resource = _parse_jid(value) | ||||
|     user = node | ||||
|     local = node | ||||
|     username = node | ||||
|  | ||||
|     server = domain | ||||
|     host = domain | ||||
|  | ||||
|     jid = full | ||||
|  | ||||
|     def __str__(self): | ||||
|         """Use the full JID as the string value.""" | ||||
|         return _format_jid(self._node, self._domain, self._resource) | ||||
|         return self._full | ||||
|  | ||||
|     def __repr__(self): | ||||
|         """Use the full JID as the representation.""" | ||||
|         return _format_jid(self._node, self._domain, self._resource) | ||||
|         return self._full | ||||
|  | ||||
|     # pylint: disable=W0212 | ||||
|     def __eq__(self, other): | ||||
| @@ -434,7 +423,10 @@ class JID: | ||||
|         if isinstance(other, UnescapedJID): | ||||
|             return False | ||||
|         if not isinstance(other, JID): | ||||
|             other = JID(other) | ||||
|             try: | ||||
|                 other = JID(other) | ||||
|             except InvalidJID: | ||||
|                 return NotImplemented | ||||
|  | ||||
|         return (self._node == other._node and | ||||
|                 self._domain == other._domain and | ||||
| @@ -446,4 +438,4 @@ class JID: | ||||
|  | ||||
|     def __hash__(self): | ||||
|         """Hash a JID based on the string version of its full JID.""" | ||||
|         return hash(_format_jid(self._node, self._domain, self._resource)) | ||||
|         return hash(self._full) | ||||
|   | ||||
| @@ -85,4 +85,6 @@ __all__ = [ | ||||
|     'xep_0323',  # IoT Systems Sensor Data | ||||
|     'xep_0325',  # IoT Systems Control | ||||
|     'xep_0332',  # HTTP Over XMPP Transport | ||||
|     'protoxep_reactions',  # https://dino.im/xeps/reactions.html | ||||
|     'protoxep_occupantid',  # https://dino.im/xeps/occupant-id.html | ||||
| ] | ||||
|   | ||||
| @@ -308,7 +308,7 @@ class BasePlugin(object): | ||||
|         if key in self.default_config: | ||||
|             self.config[key] = value | ||||
|         else: | ||||
|             super(BasePlugin, self).__setattr__(key, value) | ||||
|             super().__setattr__(key, value) | ||||
|  | ||||
|     def _init(self): | ||||
|         """Initialize plugin state, such as registering event handlers. | ||||
|   | ||||
| @@ -21,15 +21,15 @@ class GmailQuery(ElementBase): | ||||
|     namespace = 'google:mail:notify' | ||||
|     name = 'query' | ||||
|     plugin_attrib = 'gmail' | ||||
|     interfaces = set(('newer-than-time', 'newer-than-tid', 'q', 'search')) | ||||
|     interfaces = {'newer-than-time', 'newer-than-tid', 'q', 'search'} | ||||
|  | ||||
|     def getSearch(self): | ||||
|     def get_search(self): | ||||
|         return self['q'] | ||||
|  | ||||
|     def setSearch(self, search): | ||||
|     def set_search(self, search): | ||||
|         self['q'] = search | ||||
|  | ||||
|     def delSearch(self): | ||||
|     def del_search(self): | ||||
|         del self['q'] | ||||
|  | ||||
|  | ||||
| @@ -37,20 +37,20 @@ class MailBox(ElementBase): | ||||
|     namespace = 'google:mail:notify' | ||||
|     name = 'mailbox' | ||||
|     plugin_attrib = 'mailbox' | ||||
|     interfaces = set(('result-time', 'total-matched', 'total-estimate', | ||||
|                       'url', 'threads', 'matched', 'estimate')) | ||||
|     interfaces = {'result-time', 'total-matched', 'total-estimate', | ||||
|                   'url', 'threads', 'matched', 'estimate'} | ||||
|  | ||||
|     def getThreads(self): | ||||
|     def get_threads(self): | ||||
|         threads = [] | ||||
|         for threadXML in self.xml.findall('{%s}%s' % (MailThread.namespace, | ||||
|                                                       MailThread.name)): | ||||
|             threads.append(MailThread(xml=threadXML, parent=None)) | ||||
|         return threads | ||||
|  | ||||
|     def getMatched(self): | ||||
|     def get_matched(self): | ||||
|         return self['total-matched'] | ||||
|  | ||||
|     def getEstimate(self): | ||||
|     def get_estimate(self): | ||||
|         return self['total-estimate'] == '1' | ||||
|  | ||||
|  | ||||
| @@ -58,11 +58,11 @@ class MailThread(ElementBase): | ||||
|     namespace = 'google:mail:notify' | ||||
|     name = 'mail-thread-info' | ||||
|     plugin_attrib = 'thread' | ||||
|     interfaces = set(('tid', 'participation', 'messages', 'date', | ||||
|                       'senders', 'url', 'labels', 'subject', 'snippet')) | ||||
|     sub_interfaces = set(('labels', 'subject', 'snippet')) | ||||
|     interfaces = {'tid', 'participation', 'messages', 'date', | ||||
|                   'senders', 'url', 'labels', 'subject', 'snippet'} | ||||
|     sub_interfaces = {'labels', 'subject', 'snippet'} | ||||
|  | ||||
|     def getSenders(self): | ||||
|     def get_senders(self): | ||||
|         senders = [] | ||||
|         sendersXML = self.xml.find('{%s}senders' % self.namespace) | ||||
|         if sendersXML is not None: | ||||
| @@ -75,12 +75,12 @@ class MailSender(ElementBase): | ||||
|     namespace = 'google:mail:notify' | ||||
|     name = 'sender' | ||||
|     plugin_attrib = 'sender' | ||||
|     interfaces = set(('address', 'name', 'originator', 'unread')) | ||||
|     interfaces = {'address', 'name', 'originator', 'unread'} | ||||
|  | ||||
|     def getOriginator(self): | ||||
|     def get_originator(self): | ||||
|         return self.xml.attrib.get('originator', '0') == '1' | ||||
|  | ||||
|     def getUnread(self): | ||||
|     def get_unread(self): | ||||
|         return self.xml.attrib.get('unread', '0') == '1' | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										47
									
								
								slixmpp/plugins/google/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								slixmpp/plugins/google/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout | ||||
|     This file is part of slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| from slixmpp.plugins.base import register_plugin, BasePlugin | ||||
|  | ||||
| from slixmpp.plugins.google.gmail import Gmail | ||||
| from slixmpp.plugins.google.auth import GoogleAuth | ||||
| from slixmpp.plugins.google.settings import GoogleSettings | ||||
| from slixmpp.plugins.google.nosave import GoogleNoSave | ||||
|  | ||||
|  | ||||
| class Google(BasePlugin): | ||||
|  | ||||
|     """ | ||||
|     Google: Custom GTalk Features | ||||
|  | ||||
|     Also see: <https://developers.google.com/talk/jep_extensions/extensions> | ||||
|     """ | ||||
|  | ||||
|     name = 'google' | ||||
|     description = 'Google: Custom GTalk Features' | ||||
|     dependencies = set([ | ||||
|         'gmail', | ||||
|         'google_settings', | ||||
|         'google_nosave', | ||||
|         'google_auth' | ||||
|     ]) | ||||
|  | ||||
|     def __getitem__(self, attr): | ||||
|         if attr in ('settings', 'nosave', 'auth'): | ||||
|             return self.xmpp['google_%s' % attr] | ||||
|         elif attr == 'gmail': | ||||
|             return self.xmpp['gmail'] | ||||
|         else: | ||||
|             raise KeyError(attr) | ||||
|  | ||||
|  | ||||
| register_plugin(Gmail) | ||||
| register_plugin(GoogleAuth) | ||||
| register_plugin(GoogleSettings) | ||||
| register_plugin(GoogleNoSave) | ||||
| register_plugin(Google) | ||||
							
								
								
									
										10
									
								
								slixmpp/plugins/google/auth/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								slixmpp/plugins/google/auth/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout | ||||
|     This file is part of slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| from slixmpp.plugins.google.auth import stanza | ||||
| from slixmpp.plugins.google.auth.auth import GoogleAuth | ||||
							
								
								
									
										47
									
								
								slixmpp/plugins/google/auth/auth.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								slixmpp/plugins/google/auth/auth.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout | ||||
|     This file is part of slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| from slixmpp.xmlstream import register_stanza_plugin | ||||
| from slixmpp.plugins import BasePlugin | ||||
| from slixmpp.plugins.google.auth import stanza | ||||
|  | ||||
|  | ||||
| class GoogleAuth(BasePlugin): | ||||
|  | ||||
|     """ | ||||
|     Google: Auth Extensions (JID Domain Discovery, OAuth2) | ||||
|  | ||||
|     Also see: | ||||
|         <https://developers.google.com/talk/jep_extensions/jid_domain_change> | ||||
|         <https://developers.google.com/talk/jep_extensions/oauth> | ||||
|     """ | ||||
|  | ||||
|     name = 'google_auth' | ||||
|     description = 'Google: Auth Extensions (JID Domain Discovery, OAuth2)' | ||||
|     dependencies = set(['feature_mechanisms']) | ||||
|     stanza = stanza | ||||
|  | ||||
|     def plugin_init(self): | ||||
|         self.xmpp.namespace_map['http://www.google.com/talk/protocol/auth'] = 'ga' | ||||
|  | ||||
|         register_stanza_plugin(self.xmpp['feature_mechanisms'].stanza.Auth, | ||||
|                                stanza.GoogleAuth) | ||||
|  | ||||
|         self.xmpp.add_filter('out', self._auth) | ||||
|  | ||||
|     def plugin_end(self): | ||||
|         self.xmpp.del_filter('out', self._auth) | ||||
|  | ||||
|     def _auth(self, stanza): | ||||
|         if isinstance(stanza, self.xmpp['feature_mechanisms'].stanza.Auth): | ||||
|             stanza.stream = self.xmpp | ||||
|             stanza['google']['client_uses_full_bind_result'] = True | ||||
|             if stanza['mechanism'] == 'X-OAUTH2': | ||||
|                 stanza['google']['service'] = 'oauth2' | ||||
|             print(stanza) | ||||
|         return stanza | ||||
| @@ -13,7 +13,7 @@ class GoogleAuth(ElementBase): | ||||
|     name = 'auth' | ||||
|     namespace = 'http://www.google.com/talk/protocol/auth' | ||||
|     plugin_attrib = 'google' | ||||
|     interfaces = set(['client_uses_full_bind_result', 'service']) | ||||
|     interfaces = {'client_uses_full_bind_result', 'service'} | ||||
|  | ||||
|     discovery_attr= '{%s}client-uses-full-bind-result' % namespace | ||||
|     service_attr= '{%s}service' % namespace | ||||
|   | ||||
							
								
								
									
										10
									
								
								slixmpp/plugins/google/gmail/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								slixmpp/plugins/google/gmail/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout | ||||
|     This file is part of slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| from slixmpp.plugins.google.gmail import stanza | ||||
| from slixmpp.plugins.google.gmail.notifications import Gmail | ||||
							
								
								
									
										101
									
								
								slixmpp/plugins/google/gmail/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								slixmpp/plugins/google/gmail/stanza.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout | ||||
|     This file is part of slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| from slixmpp.xmlstream import ElementBase, register_stanza_plugin | ||||
|  | ||||
|  | ||||
| class GmailQuery(ElementBase): | ||||
|     namespace = 'google:mail:notify' | ||||
|     name = 'query' | ||||
|     plugin_attrib = 'gmail' | ||||
|     interfaces = set(['newer_than_time', 'newer_than_tid', 'search']) | ||||
|  | ||||
|     def get_search(self): | ||||
|         return self._get_attr('q', '') | ||||
|  | ||||
|     def set_search(self, search): | ||||
|         self._set_attr('q', search) | ||||
|  | ||||
|     def del_search(self): | ||||
|         self._del_attr('q') | ||||
|  | ||||
|     def get_newer_than_time(self): | ||||
|         return self._get_attr('newer-than-time', '') | ||||
|  | ||||
|     def set_newer_than_time(self, value): | ||||
|         self._set_attr('newer-than-time', value) | ||||
|  | ||||
|     def del_newer_than_time(self): | ||||
|         self._del_attr('newer-than-time') | ||||
|  | ||||
|     def get_newer_than_tid(self): | ||||
|         return self._get_attr('newer-than-tid', '') | ||||
|  | ||||
|     def set_newer_than_tid(self, value): | ||||
|         self._set_attr('newer-than-tid', value) | ||||
|  | ||||
|     def del_newer_than_tid(self): | ||||
|         self._del_attr('newer-than-tid') | ||||
|  | ||||
|  | ||||
| class MailBox(ElementBase): | ||||
|     namespace = 'google:mail:notify' | ||||
|     name = 'mailbox' | ||||
|     plugin_attrib = 'gmail_messages' | ||||
|     interfaces = set(['result_time', 'url', 'matched', 'estimate']) | ||||
|  | ||||
|     def get_matched(self): | ||||
|         return self._get_attr('total-matched', '') | ||||
|  | ||||
|     def get_estimate(self): | ||||
|         return self._get_attr('total-estimate', '') == '1' | ||||
|  | ||||
|     def get_result_time(self): | ||||
|         return self._get_attr('result-time', '') | ||||
|  | ||||
|  | ||||
| class MailThread(ElementBase): | ||||
|     namespace = 'google:mail:notify' | ||||
|     name = 'mail-thread-info' | ||||
|     plugin_attrib = 'thread' | ||||
|     plugin_multi_attrib = 'threads' | ||||
|     interfaces = set(['tid', 'participation', 'messages', 'date', | ||||
|                       'senders', 'url', 'labels', 'subject', 'snippet']) | ||||
|     sub_interfaces = set(['labels', 'subject', 'snippet']) | ||||
|  | ||||
|     def get_senders(self): | ||||
|         result = [] | ||||
|         senders = self.xml.findall('{%s}senders/{%s}sender' % ( | ||||
|             self.namespace, self.namespace)) | ||||
|  | ||||
|         for sender in senders: | ||||
|             result.append(MailSender(xml=sender)) | ||||
|  | ||||
|         return result | ||||
|  | ||||
|  | ||||
| class MailSender(ElementBase): | ||||
|     namespace = 'google:mail:notify' | ||||
|     name = 'sender' | ||||
|     plugin_attrib = name | ||||
|     interfaces = set(['address', 'name', 'originator', 'unread']) | ||||
|  | ||||
|     def get_originator(self): | ||||
|         return self.xml.attrib.get('originator', '0') == '1' | ||||
|  | ||||
|     def get_unread(self): | ||||
|         return self.xml.attrib.get('unread', '0') == '1' | ||||
|  | ||||
|  | ||||
| class NewMail(ElementBase): | ||||
|     namespace = 'google:mail:notify' | ||||
|     name = 'new-mail' | ||||
|     plugin_attrib = 'gmail_notification' | ||||
|  | ||||
|  | ||||
| register_stanza_plugin(MailBox, MailThread, iterable=True) | ||||
							
								
								
									
										10
									
								
								slixmpp/plugins/google/nosave/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								slixmpp/plugins/google/nosave/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout | ||||
|     This file is part of slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| from slixmpp.plugins.google.nosave import stanza | ||||
| from slixmpp.plugins.google.nosave.nosave import GoogleNoSave | ||||
							
								
								
									
										78
									
								
								slixmpp/plugins/google/nosave/nosave.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								slixmpp/plugins/google/nosave/nosave.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout | ||||
|     This file is part of slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| from slixmpp.stanza import Iq, Message | ||||
| from slixmpp.xmlstream.handler import Callback | ||||
| from slixmpp.xmlstream.matcher import StanzaPath | ||||
| from slixmpp.xmlstream import register_stanza_plugin | ||||
| from slixmpp.plugins import BasePlugin | ||||
| from slixmpp.plugins.google.nosave import stanza | ||||
|  | ||||
|  | ||||
| class GoogleNoSave(BasePlugin): | ||||
|  | ||||
|     """ | ||||
|     Google: Off the Record Chats | ||||
|  | ||||
|     NOTE: This is NOT an encryption method. | ||||
|  | ||||
|     Also see <https://developers.google.com/talk/jep_extensions/otr>. | ||||
|     """ | ||||
|  | ||||
|     name = 'google_nosave' | ||||
|     description = 'Google: Off the Record Chats' | ||||
|     dependencies = set(['google_settings']) | ||||
|     stanza = stanza | ||||
|  | ||||
|     def plugin_init(self): | ||||
|         register_stanza_plugin(Message, stanza.NoSave) | ||||
|         register_stanza_plugin(Iq, stanza.NoSaveQuery) | ||||
|  | ||||
|         self.xmpp.register_handler( | ||||
|                 Callback('Google Nosave', | ||||
|                     StanzaPath('iq@type=set/google_nosave'), | ||||
|                     self._handle_nosave_change)) | ||||
|  | ||||
|     def plugin_end(self): | ||||
|         self.xmpp.remove_handler('Google Nosave') | ||||
|  | ||||
|     def enable(self, jid=None, timeout=None, callback=None): | ||||
|         if jid is None: | ||||
|             self.xmpp['google_settings'].update({'archiving_enabled': False}, | ||||
|                     timeout=timeout, callback=callback) | ||||
|         else: | ||||
|             iq = self.xmpp.Iq() | ||||
|             iq['type'] = 'set' | ||||
|             iq['google_nosave']['item']['jid'] = jid | ||||
|             iq['google_nosave']['item']['value'] = True | ||||
|             return iq.send(timeout=timeout, callback=callback) | ||||
|  | ||||
|     def disable(self, jid=None, timeout=None, callback=None): | ||||
|         if jid is None: | ||||
|             self.xmpp['google_settings'].update({'archiving_enabled': True}, | ||||
|                     timeout=timeout, callback=callback) | ||||
|         else: | ||||
|             iq = self.xmpp.Iq() | ||||
|             iq['type'] = 'set' | ||||
|             iq['google_nosave']['item']['jid'] = jid | ||||
|             iq['google_nosave']['item']['value'] = False | ||||
|             return iq.send(timeout=timeout, callback=callback) | ||||
|  | ||||
|     def get(self, timeout=None, callback=None): | ||||
|         iq = self.xmpp.Iq() | ||||
|         iq['type'] = 'get' | ||||
|         iq.enable('google_nosave') | ||||
|         return iq.send(timeout=timeout, callback=callback) | ||||
|  | ||||
|     def _handle_nosave_change(self, iq): | ||||
|         reply = self.xmpp.Iq() | ||||
|         reply['type'] = 'result' | ||||
|         reply['id'] = iq['id'] | ||||
|         reply['to'] = iq['from'] | ||||
|         reply.send() | ||||
|         self.xmpp.event('google_nosave_change', iq) | ||||
| @@ -14,7 +14,7 @@ class NoSave(ElementBase): | ||||
|     name = 'x' | ||||
|     namespace = 'google:nosave' | ||||
|     plugin_attrib = 'google_nosave' | ||||
|     interfaces = set(['value']) | ||||
|     interfaces = {'value'} | ||||
|  | ||||
|     def get_value(self): | ||||
|         return self._get_attr('value', '') == 'enabled' | ||||
| @@ -35,7 +35,7 @@ class Item(ElementBase): | ||||
|     namespace = 'google:nosave' | ||||
|     plugin_attrib = 'item' | ||||
|     plugin_multi_attrib = 'items' | ||||
|     interfaces = set(['jid', 'source', 'value']) | ||||
|     interfaces = {'jid', 'source', 'value'} | ||||
|  | ||||
|     def get_value(self): | ||||
|         return self._get_attr('value', '') == 'enabled' | ||||
|   | ||||
							
								
								
									
										10
									
								
								slixmpp/plugins/google/settings/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								slixmpp/plugins/google/settings/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout | ||||
|     This file is part of slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| from slixmpp.plugins.google.settings import stanza | ||||
| from slixmpp.plugins.google.settings.settings import GoogleSettings | ||||
							
								
								
									
										110
									
								
								slixmpp/plugins/google/settings/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								slixmpp/plugins/google/settings/stanza.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2013 Nathanael C. Fritz, Lance J.T. Stout | ||||
|     This file is part of slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| from slixmpp.xmlstream import ET, ElementBase | ||||
|  | ||||
|  | ||||
| class UserSettings(ElementBase): | ||||
|     name = 'usersetting' | ||||
|     namespace = 'google:setting' | ||||
|     plugin_attrib = 'google_settings' | ||||
|     interfaces = set(['auto_accept_suggestions', | ||||
|                       'mail_notifications', | ||||
|                       'archiving_enabled', | ||||
|                       'gmail', | ||||
|                       'email_verified', | ||||
|                       'domain_privacy_notice', | ||||
|                       'display_name']) | ||||
|  | ||||
|     def _get_setting(self, setting): | ||||
|         xml = self.xml.find('{%s}%s' % (self.namespace, setting)) | ||||
|         if xml is not None: | ||||
|             return xml.attrib.get('value', '') == 'true' | ||||
|         return False | ||||
|  | ||||
|     def _set_setting(self, setting, value): | ||||
|         self._del_setting(setting) | ||||
|         if value in (True, False): | ||||
|             xml = ET.Element('{%s}%s' % (self.namespace, setting)) | ||||
|             xml.attrib['value'] = 'true' if value else 'false' | ||||
|             self.xml.append(xml) | ||||
|  | ||||
|     def _del_setting(self, setting): | ||||
|         xml = self.xml.find('{%s}%s' % (self.namespace, setting)) | ||||
|         if xml is not None: | ||||
|             self.xml.remove(xml) | ||||
|  | ||||
|     def get_display_name(self): | ||||
|         xml = self.xml.find('{%s}%s' % (self.namespace, 'displayname')) | ||||
|         if xml is not None: | ||||
|             return xml.attrib.get('value', '') | ||||
|         return '' | ||||
|  | ||||
|     def set_display_name(self, value): | ||||
|         self._del_setting(setting) | ||||
|         if value: | ||||
|             xml = ET.Element('{%s}%s' % (self.namespace, 'displayname')) | ||||
|             xml.attrib['value'] = value | ||||
|             self.xml.append(xml) | ||||
|  | ||||
|     def del_display_name(self): | ||||
|         self._del_setting('displayname') | ||||
|  | ||||
|     def get_auto_accept_suggestions(self): | ||||
|         return self._get_setting('autoacceptsuggestions') | ||||
|  | ||||
|     def get_mail_notifications(self): | ||||
|         return self._get_setting('mailnotifications') | ||||
|  | ||||
|     def get_archiving_enabled(self): | ||||
|         return self._get_setting('archivingenabled') | ||||
|  | ||||
|     def get_gmail(self): | ||||
|         return self._get_setting('gmail') | ||||
|  | ||||
|     def get_email_verified(self): | ||||
|         return self._get_setting('emailverified') | ||||
|  | ||||
|     def get_domain_privacy_notice(self): | ||||
|         return self._get_setting('domainprivacynotice') | ||||
|  | ||||
|     def set_auto_accept_suggestions(self, value): | ||||
|         self._set_setting('autoacceptsuggestions', value) | ||||
|  | ||||
|     def set_mail_notifications(self, value): | ||||
|         self._set_setting('mailnotifications', value) | ||||
|  | ||||
|     def set_archiving_enabled(self, value): | ||||
|         self._set_setting('archivingenabled', value) | ||||
|  | ||||
|     def set_gmail(self, value): | ||||
|         self._set_setting('gmail', value) | ||||
|  | ||||
|     def set_email_verified(self, value): | ||||
|         self._set_setting('emailverified', value) | ||||
|  | ||||
|     def set_domain_privacy_notice(self, value): | ||||
|         self._set_setting('domainprivacynotice', value) | ||||
|  | ||||
|     def del_auto_accept_suggestions(self): | ||||
|         self._del_setting('autoacceptsuggestions') | ||||
|  | ||||
|     def del_mail_notifications(self): | ||||
|         self._del_setting('mailnotifications') | ||||
|  | ||||
|     def del_archiving_enabled(self): | ||||
|         self._del_setting('archivingenabled') | ||||
|  | ||||
|     def del_gmail(self): | ||||
|         self._del_setting('gmail') | ||||
|  | ||||
|     def del_email_verified(self): | ||||
|         self._del_setting('emailverified') | ||||
|  | ||||
|     def del_domain_privacy_notice(self): | ||||
|         self._del_setting('domainprivacynotice') | ||||
							
								
								
									
										12
									
								
								slixmpp/plugins/protoxep_occupantid/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								slixmpp/plugins/protoxep_occupantid/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2019 Mathieu Pasquet | ||||
|     This file is part of Slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
| from slixmpp.plugins.base import register_plugin | ||||
| from slixmpp.plugins.protoxep_occupantid.occupantid import XEP_OccupantID | ||||
| from slixmpp.plugins.protoxep_occupantid.stanza import OccupantID | ||||
|  | ||||
| register_plugin(XEP_OccupantID) | ||||
							
								
								
									
										23
									
								
								slixmpp/plugins/protoxep_occupantid/occupantid.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								slixmpp/plugins/protoxep_occupantid/occupantid.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2019 Mathieu Pasquet | ||||
|     This file is part of Slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
| from slixmpp.plugins import BasePlugin | ||||
| from slixmpp.stanza import Message, Presence | ||||
| from slixmpp.xmlstream import register_stanza_plugin | ||||
|  | ||||
| from slixmpp.plugins.protoxep_occupantid import stanza | ||||
|  | ||||
|  | ||||
| class XEP_OccupantID(BasePlugin): | ||||
|     name = 'protoxep_occupantid' | ||||
|     description = 'XEP-XXXX: Anonymous unique occupant identifiers for MUCs' | ||||
|     dependencies = set() | ||||
|     stanza = stanza | ||||
|  | ||||
|     def plugin_init(self): | ||||
|         register_stanza_plugin(Message, stanza.OccupantID) | ||||
|         register_stanza_plugin(Presence, stanza.OccupantID) | ||||
							
								
								
									
										16
									
								
								slixmpp/plugins/protoxep_occupantid/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								slixmpp/plugins/protoxep_occupantid/stanza.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2019 Mathieu Pasquet | ||||
|     This file is part of Slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| from slixmpp.xmlstream import ElementBase | ||||
|  | ||||
|  | ||||
| class OccupantID(ElementBase): | ||||
|     name = 'occupant-id' | ||||
|     plugin_attrib = 'occupant-id' | ||||
|     namespace = 'urn:xmpp:occupant-id:0' | ||||
|     interfaces = {'id'} | ||||
							
								
								
									
										11
									
								
								slixmpp/plugins/protoxep_reactions/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								slixmpp/plugins/protoxep_reactions/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2019 Mathieu Pasquet | ||||
|     This file is part of Slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
| from slixmpp.plugins.base import register_plugin | ||||
| from slixmpp.plugins.protoxep_reactions.reactions import XEP_Reactions | ||||
|  | ||||
| register_plugin(XEP_Reactions) | ||||
							
								
								
									
										54
									
								
								slixmpp/plugins/protoxep_reactions/reactions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								slixmpp/plugins/protoxep_reactions/reactions.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2019 Mathieu Pasquet | ||||
|     This file is part of Slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
| from typing import Iterable | ||||
|  | ||||
| from slixmpp.plugins import BasePlugin | ||||
| from slixmpp.stanza import Message | ||||
| from slixmpp.xmlstream import register_stanza_plugin | ||||
| from slixmpp.xmlstream.matcher import MatchXMLMask | ||||
| from slixmpp.xmlstream.handler import Callback | ||||
|  | ||||
| from slixmpp.plugins.protoxep_reactions import stanza | ||||
|  | ||||
|  | ||||
| class XEP_Reactions(BasePlugin): | ||||
|     name = 'protoxep_reactions' | ||||
|     description = 'XEP-XXXX: Message Reactions' | ||||
|     dependencies = {'xep_0030'} | ||||
|     stanza = stanza | ||||
|  | ||||
|     def plugin_init(self): | ||||
|         self.xmpp.register_handler( | ||||
|             Callback( | ||||
|                 'Reaction received', | ||||
|                 MatchXMLMask('<message><reactions xmlns="urn:xmpp:reactions:0"/></message>'), | ||||
|                 self._handle_reactions, | ||||
|             ) | ||||
|         ) | ||||
|         self.xmpp['xep_0030'].add_feature('urn:xmpp:reactions:0') | ||||
|         register_stanza_plugin(Message, stanza.Reactions) | ||||
|  | ||||
|     def plugin_end(self): | ||||
|         self.xmpp.remove_handler('Reaction received') | ||||
|         self.xmpp['xep_0030'].remove_feature('urn:xmpp:reactions:0') | ||||
|  | ||||
|     def _handle_reactions(self, message: Message): | ||||
|         self.xmpp.event('reactions', message) | ||||
|  | ||||
|     @staticmethod | ||||
|     def set_reactions(message: Message, to_id: str, reactions: Iterable[str]): | ||||
|         """ | ||||
|         Add reactions to a Message object. | ||||
|         """ | ||||
|         reactions_stanza = stanza.Reactions() | ||||
|         reactions_stanza['to'] = to_id | ||||
|         for reaction in reactions: | ||||
|             reaction_stanza = stanza.Reaction() | ||||
|             reaction_stanza['value'] = reaction | ||||
|             reactions_stanza.append(reaction_stanza) | ||||
|         message.append(reactions_stanza) | ||||
							
								
								
									
										31
									
								
								slixmpp/plugins/protoxep_reactions/stanza.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								slixmpp/plugins/protoxep_reactions/stanza.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| """ | ||||
|     Slixmpp: The Slick XMPP Library | ||||
|     Copyright (C) 2019 Mathieu Pasquet | ||||
|     This file is part of Slixmpp. | ||||
|  | ||||
|     See the file LICENSE for copying permission. | ||||
| """ | ||||
|  | ||||
| from slixmpp.xmlstream import ElementBase, register_stanza_plugin | ||||
|  | ||||
|  | ||||
| class Reactions(ElementBase): | ||||
|     name = 'reactions' | ||||
|     plugin_attrib = 'reactions' | ||||
|     namespace = 'urn:xmpp:reactions:0' | ||||
|     interfaces = {'to'} | ||||
|  | ||||
|  | ||||
| class Reaction(ElementBase): | ||||
|     name = 'reaction' | ||||
|     namespace = 'urn:xmpp:reactions:0' | ||||
|     interfaces = {'value'} | ||||
|  | ||||
|     def get_value(self) -> str: | ||||
|         return self.xml.text | ||||
|  | ||||
|     def set_value(self, value: str): | ||||
|         self.xml.text = value | ||||
|  | ||||
|  | ||||
| register_stanza_plugin(Reactions, Reaction, iterable=True) | ||||
| @@ -14,9 +14,3 @@ from slixmpp.plugins.xep_0004.dataforms import XEP_0004 | ||||
|  | ||||
|  | ||||
| register_plugin(XEP_0004) | ||||
|  | ||||
|  | ||||
| # Retain some backwards compatibility | ||||
| xep_0004 = XEP_0004 | ||||
| xep_0004.makeForm = xep_0004.make_form | ||||
| xep_0004.buildForm = xep_0004.build_form | ||||
|   | ||||
| @@ -23,7 +23,7 @@ class XEP_0004(BasePlugin): | ||||
|  | ||||
|     name = 'xep_0004' | ||||
|     description = 'XEP-0004: Data Forms' | ||||
|     dependencies = set(['xep_0030']) | ||||
|     dependencies = {'xep_0030'} | ||||
|     stanza = stanza | ||||
|  | ||||
|     def plugin_init(self): | ||||
|   | ||||
| @@ -14,21 +14,21 @@ class FormField(ElementBase): | ||||
|     name = 'field' | ||||
|     plugin_attrib = 'field' | ||||
|     plugin_multi_attrib = 'fields' | ||||
|     interfaces = set(('answer', 'desc', 'required', 'value', | ||||
|                       'label', 'type', 'var')) | ||||
|     sub_interfaces = set(('desc',)) | ||||
|     interfaces = {'answer', 'desc', 'required', 'value', | ||||
|                   'label', 'type', 'var'} | ||||
|     sub_interfaces = {'desc'} | ||||
|     plugin_tag_map = {} | ||||
|     plugin_attrib_map = {} | ||||
|  | ||||
|     field_types = set(('boolean', 'fixed', 'hidden', 'jid-multi', | ||||
|                        'jid-single', 'list-multi', 'list-single', | ||||
|                        'text-multi', 'text-private', 'text-single')) | ||||
|     field_types = {'boolean', 'fixed', 'hidden', 'jid-multi', | ||||
|                    'jid-single', 'list-multi', 'list-single', | ||||
|                    'text-multi', 'text-private', 'text-single'} | ||||
|  | ||||
|     true_values = set((True, '1', 'true')) | ||||
|     option_types = set(('list-multi', 'list-single')) | ||||
|     multi_line_types = set(('hidden', 'text-multi')) | ||||
|     multi_value_types = set(('hidden', 'jid-multi', | ||||
|                              'list-multi', 'text-multi')) | ||||
|     true_values = {True, '1', 'true'} | ||||
|     option_types = {'list-multi', 'list-single'} | ||||
|     multi_line_types = {'hidden', 'text-multi'} | ||||
|     multi_value_types = {'hidden', 'jid-multi', | ||||
|                          'list-multi', 'text-multi'} | ||||
|  | ||||
|     def setup(self, xml=None): | ||||
|         if ElementBase.setup(self, xml): | ||||
| @@ -164,8 +164,8 @@ class FieldOption(ElementBase): | ||||
|     namespace = 'jabber:x:data' | ||||
|     name = 'option' | ||||
|     plugin_attrib = 'option' | ||||
|     interfaces = set(('label', 'value')) | ||||
|     sub_interfaces = set(('value',)) | ||||
|     interfaces = {'label', 'value'} | ||||
|     sub_interfaces = {'value'} | ||||
|     plugin_multi_attrib = 'options' | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -23,9 +23,9 @@ class Form(ElementBase): | ||||
|     namespace = 'jabber:x:data' | ||||
|     name = 'x' | ||||
|     plugin_attrib = 'form' | ||||
|     interfaces = OrderedSet(('instructions', 'reported', 'title', 'type', 'items', )) | ||||
|     sub_interfaces = set(('title',)) | ||||
|     form_types = set(('cancel', 'form', 'result', 'submit')) | ||||
|     interfaces = OrderedSet(('instructions', 'reported', 'title', 'type', 'items', 'values')) | ||||
|     sub_interfaces = {'title'} | ||||
|     form_types = {'cancel', 'form', 'result', 'submit'} | ||||
|  | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         title = None | ||||
| @@ -81,18 +81,6 @@ class Form(ElementBase): | ||||
|         self.append(field) | ||||
|         return field | ||||
|  | ||||
|     def getXML(self, type='submit'): | ||||
|         self['type'] = type | ||||
|         log.warning("Form.getXML() is deprecated API compatibility " + \ | ||||
|                     "with plugins/old_0004.py") | ||||
|         return self.xml | ||||
|  | ||||
|     def fromXML(self, xml): | ||||
|         log.warning("Form.fromXML() is deprecated API compatibility " + \ | ||||
|                     "with plugins/old_0004.py") | ||||
|         n = Form(xml=xml) | ||||
|         return n | ||||
|  | ||||
|     def add_item(self, values): | ||||
|         itemXML = ET.Element('{%s}item' % self.namespace) | ||||
|         self.xml.append(itemXML) | ||||
|   | ||||
| @@ -14,7 +14,3 @@ from slixmpp.plugins.xep_0009.stanza import RPCQuery, MethodCall, MethodResponse | ||||
|  | ||||
|  | ||||
| register_plugin(XEP_0009) | ||||
|  | ||||
|  | ||||
| # Retain some backwards compatibility | ||||
| xep_0009 = XEP_0009 | ||||
|   | ||||
| @@ -25,7 +25,7 @@ def fault2xml(fault): | ||||
|  | ||||
| def xml2fault(params): | ||||
|     vals = [] | ||||
|     for value in params.findall('{%s}value' % _namespace): | ||||
|     for value in params.xml.findall('{%s}value' % _namespace): | ||||
|         vals.append(_xml2py(value)) | ||||
|     fault = dict() | ||||
|     fault['code'] = vals[0]['faultCode'] | ||||
| @@ -98,33 +98,34 @@ def xml2py(params): | ||||
|  | ||||
| def _xml2py(value): | ||||
|     namespace = 'jabber:iq:rpc' | ||||
|     if value.find('{%s}nil' % namespace) is not None: | ||||
|     find_value = value.find | ||||
|     if find_value('{%s}nil' % namespace) is not None: | ||||
|         return None | ||||
|     if value.find('{%s}i4' % namespace) is not None: | ||||
|         return int(value.find('{%s}i4' % namespace).text) | ||||
|     if value.find('{%s}int' % namespace) is not None: | ||||
|         return int(value.find('{%s}int' % namespace).text) | ||||
|     if value.find('{%s}boolean' % namespace) is not None: | ||||
|         return bool(int(value.find('{%s}boolean' % namespace).text)) | ||||
|     if value.find('{%s}string' % namespace) is not None: | ||||
|         return value.find('{%s}string' % namespace).text | ||||
|     if value.find('{%s}double' % namespace) is not None: | ||||
|         return float(value.find('{%s}double' % namespace).text) | ||||
|     if value.find('{%s}base64' % namespace) is not None: | ||||
|         return rpcbase64(value.find('{%s}base64' % namespace).text.encode()) | ||||
|     if value.find('{%s}Base64' % namespace) is not None: | ||||
|     if find_value('{%s}i4' % namespace) is not None: | ||||
|         return int(find_value('{%s}i4' % namespace).text) | ||||
|     if find_value('{%s}int' % namespace) is not None: | ||||
|         return int(find_value('{%s}int' % namespace).text) | ||||
|     if find_value('{%s}boolean' % namespace) is not None: | ||||
|         return bool(int(find_value('{%s}boolean' % namespace).text)) | ||||
|     if find_value('{%s}string' % namespace) is not None: | ||||
|         return find_value('{%s}string' % namespace).text | ||||
|     if find_value('{%s}double' % namespace) is not None: | ||||
|         return float(find_value('{%s}double' % namespace).text) | ||||
|     if find_value('{%s}base64' % namespace) is not None: | ||||
|         return rpcbase64(find_value('{%s}base64' % namespace).text.encode()) | ||||
|     if find_value('{%s}Base64' % namespace) is not None: | ||||
|         # Older versions of XEP-0009 used Base64 | ||||
|         return rpcbase64(value.find('{%s}Base64' % namespace).text.encode()) | ||||
|     if value.find('{%s}dateTime.iso8601' % namespace) is not None: | ||||
|         return rpctime(value.find('{%s}dateTime.iso8601' % namespace).text) | ||||
|     if value.find('{%s}struct' % namespace) is not None: | ||||
|         return rpcbase64(find_value('{%s}Base64' % namespace).text.encode()) | ||||
|     if find_value('{%s}dateTime.iso8601' % namespace) is not None: | ||||
|         return rpctime(find_value('{%s}dateTime.iso8601' % namespace).text) | ||||
|     if find_value('{%s}struct' % namespace) is not None: | ||||
|         struct = {} | ||||
|         for member in value.find('{%s}struct' % namespace).findall('{%s}member' % namespace): | ||||
|         for member in find_value('{%s}struct' % namespace).findall('{%s}member' % namespace): | ||||
|             struct[member.find('{%s}name' % namespace).text] = _xml2py(member.find('{%s}value' % namespace)) | ||||
|         return struct | ||||
|     if value.find('{%s}array' % namespace) is not None: | ||||
|     if find_value('{%s}array' % namespace) is not None: | ||||
|         array = [] | ||||
|         for val in value.find('{%s}array' % namespace).find('{%s}data' % namespace).findall('{%s}value' % namespace): | ||||
|         for val in find_value('{%s}array' % namespace).find('{%s}data' % namespace).findall('{%s}value' % namespace): | ||||
|             array.append(_xml2py(val)) | ||||
|         return array | ||||
|     raise ValueError() | ||||
|   | ||||
| @@ -61,7 +61,7 @@ def _intercept(method, name, public): | ||||
|         except InvocationException: | ||||
|             raise | ||||
|         except Exception as e: | ||||
|             raise InvocationException("A problem occured calling %s.%s!" % (instance.FQN(), method.__name__), e) | ||||
|             raise InvocationException("A problem occurred calling %s.%s!" % (instance.FQN(), method.__name__), e) | ||||
|     _resolver._rpc = public | ||||
|     _resolver._rpc_name = method.__name__ if name is None else name | ||||
|     return _resolver | ||||
| @@ -163,7 +163,7 @@ class ACL: | ||||
|  | ||||
|     @classmethod | ||||
|     def _next_token(cls, expression, index): | ||||
|         new_index = expression.find('*', index) | ||||
|         new_index = expression.xml.find('*', index) | ||||
|         if new_index == 0: | ||||
|             return '' | ||||
|         else: | ||||
| @@ -182,7 +182,7 @@ class ACL: | ||||
|             #! print "[TOKEN] '%s'" % token | ||||
|             size = len(token) | ||||
|             if size > 0: | ||||
|                 token_index = value.find(token, position) | ||||
|                 token_index = value.xml.find(token, position) | ||||
|                 if token_index == -1: | ||||
|                     return False | ||||
|                 else: | ||||
| @@ -405,8 +405,10 @@ class Proxy(Endpoint): | ||||
|         self._callback = callback | ||||
|  | ||||
|     def __getattribute__(self, name, *args): | ||||
|         if name in ('__dict__', '_endpoint', 'async', '_callback'): | ||||
|         if name in ('__dict__', '_endpoint', '_callback'): | ||||
|             return object.__getattribute__(self, name) | ||||
|         elif name == 'async': | ||||
|             return lambda callback: Proxy(self._endpoint, callback) | ||||
|         else: | ||||
|             attribute = self._endpoint.__getattribute__(name) | ||||
|             if hasattr(attribute, '__call__'): | ||||
| @@ -420,9 +422,6 @@ class Proxy(Endpoint): | ||||
|                     pass   # If the attribute doesn't exist, don't care! | ||||
|             return attribute | ||||
|  | ||||
|     def async(self, callback): | ||||
|         return Proxy(self._endpoint, callback) | ||||
|  | ||||
|     def get_endpoint(self): | ||||
|         ''' | ||||
|         Returns the proxified endpoint. | ||||
| @@ -696,7 +695,7 @@ class RemoteSession(object): | ||||
|         e = { | ||||
|             'item-not-found': RemoteException("No remote handler available for %s at %s!" % (pmethod, iq['from'])), | ||||
|             'forbidden': AuthorizationException("Forbidden to invoke remote handler for %s at %s!" % (pmethod, iq['from'])), | ||||
|             'undefined-condition': RemoteException("An unexpected problem occured trying to invoke %s at %s!" % (pmethod, iq['from'])), | ||||
|             'undefined-condition': RemoteException("An unexpected problem occurred trying to invoke %s at %s!" % (pmethod, iq['from'])), | ||||
|         }[condition] | ||||
|         if e is None: | ||||
|             RemoteException("An unexpected exception occurred at %s!" % iq['from']) | ||||
|   | ||||
| @@ -24,7 +24,7 @@ class XEP_0009(BasePlugin): | ||||
|  | ||||
|     name = 'xep_0009' | ||||
|     description = 'XEP-0009: Jabber-RPC' | ||||
|     dependencies = set(['xep_0030']) | ||||
|     dependencies = {'xep_0030'} | ||||
|     stanza = stanza | ||||
|  | ||||
|     def plugin_init(self): | ||||
| @@ -121,7 +121,7 @@ class XEP_0009(BasePlugin): | ||||
|     def _recipient_unvailable(self, iq): | ||||
|         payload = iq.get_payload() | ||||
|         iq = iq.reply() | ||||
|         error().set_payload(payload) | ||||
|         iq.error().set_payload(payload) | ||||
|         iq['error']['code'] = '404' | ||||
|         iq['error']['type'] = 'wait' | ||||
|         iq['error']['condition'] = 'recipient-unavailable' | ||||
|   | ||||
| @@ -7,15 +7,15 @@ | ||||
| """ | ||||
|  | ||||
| from slixmpp.xmlstream.stanzabase import ElementBase | ||||
| from xml.etree import cElementTree as ET | ||||
| from xml.etree import ElementTree as ET | ||||
|  | ||||
|  | ||||
| class RPCQuery(ElementBase): | ||||
|     name = 'query' | ||||
|     namespace = 'jabber:iq:rpc' | ||||
|     plugin_attrib = 'rpc_query' | ||||
|     interfaces = set(()) | ||||
|     subinterfaces = set(()) | ||||
|     interfaces = {} | ||||
|     subinterfaces = {} | ||||
|     plugin_attrib_map = {} | ||||
|     plugin_tag_map = {} | ||||
|  | ||||
| @@ -24,8 +24,8 @@ class MethodCall(ElementBase): | ||||
|     name = 'methodCall' | ||||
|     namespace = 'jabber:iq:rpc' | ||||
|     plugin_attrib = 'method_call' | ||||
|     interfaces = set(('method_name', 'params')) | ||||
|     subinterfaces = set(()) | ||||
|     interfaces = {'method_name', 'params'} | ||||
|     subinterfaces = {} | ||||
|     plugin_attrib_map = {} | ||||
|     plugin_tag_map = {} | ||||
|  | ||||
| @@ -46,8 +46,8 @@ class MethodResponse(ElementBase): | ||||
|     name = 'methodResponse' | ||||
|     namespace = 'jabber:iq:rpc' | ||||
|     plugin_attrib = 'method_response' | ||||
|     interfaces = set(('params', 'fault')) | ||||
|     subinterfaces = set(()) | ||||
|     interfaces = {'params', 'fault'} | ||||
|     subinterfaces = {} | ||||
|     plugin_attrib_map = {} | ||||
|     plugin_tag_map = {} | ||||
|  | ||||
|   | ||||
| @@ -13,7 +13,3 @@ from slixmpp.plugins.xep_0012.last_activity import XEP_0012 | ||||
|  | ||||
|  | ||||
| register_plugin(XEP_0012) | ||||
|  | ||||
|  | ||||
| # Retain some backwards compatibility | ||||
| xep_0004 = XEP_0012 | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user