Compare commits

..

457 Commits

Author SHA1 Message Date
mathieui
3dcb96d9d8 Update version to 1.5.0 2020-05-01 15:26:24 +02:00
Maxime Buquet
0a7a4c3abe Merge branch 'python-version' into 'master'
Update Python version minimum requirement to 3.7

See merge request poezio/slixmpp!42
2020-05-01 15:14:49 +02:00
Maxime “pep” Buquet
a4bbc404ed Update Python version minimum requirement to 3.7
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2020-05-01 15:13:19 +02:00
Maxime “pep” Buquet
c3fbc6cb80 xep_0196: Use correct tag local name (thanks ivucica)
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2020-04-06 12:57:12 +02:00
Maxime “pep” Buquet
355d789061 docs: remove andyet logo/link on every page
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2020-04-06 12:57:12 +02:00
mathieui
47ed67c04e Merge branch 'add-github-pr-template' into 'master'
Add a github pull request template

See merge request poezio/slixmpp!41
2020-04-04 20:06:23 +02:00
mathieui
34567f450a Add a github pull request template 2020-04-04 20:04:23 +02:00
mathieui
9126bd8392 Merge branch 'fix-deprecations' into 'master'
Fix deprecation warning regarding invalid escape sequences.

See merge request poezio/slixmpp!40
2020-04-04 18:53:15 +02:00
mathieui
02202f7cd8 Merge branch 'fix-nonetype-error' into 'master'
Fix an issue when deleting subelements: TypeError: 'NoneType' object is not an iterator

See merge request poezio/slixmpp!39
2020-04-04 18:44:25 +02:00
jheling
2add94f5b0 Fix TypeError: 'NoneType' object is not an iterator
When deleting sub-elements in a stanza.
2020-04-04 18:42:48 +02:00
Karthikeyan Singaravelan
5fc757f200 Fix deprecation warning regarding invalid escape sequences. 2020-04-04 16:08:44 +00:00
mathieui
98108d0445 Merge branch 'fix-celementtree-import' into 'master'
cElementTree has been deprecated since Python 3.3 and removed in Python 3.9.

See merge request poezio/slixmpp!38
2020-04-04 17:55:11 +02:00
Maxime Buquet
76f4fb49d6 Merge branch 'sync-fixes' into 'master'
Sync fixes

See merge request poezio/slixmpp!37
2020-04-04 15:28:59 +02:00
Georg Lukas
5be46a5e68 fire 'disconnected' callback from abort() 2020-04-04 13:16:33 +02:00
Georg Lukas
ab9040c30e expose is_connecting state based on connection attempt future 2020-04-04 13:16:31 +02:00
Maxime Buquet
a16e2a0f6c Merge branch 'fix-0198' into 'master'
XEP-0198: unset end_session_on_disconnect on resume/enable

See merge request poezio/slixmpp!36
2020-03-29 14:28:06 +02:00
Maxime Buquet
842aa3be8f Merge branch 'fix-reconnect-2.0' into 'master'
Reset reconnect delay on manual reconnect, add delay event

See merge request poezio/slixmpp!35
2020-03-29 14:26:15 +02:00
Georg Lukas
6c28b49e7f XEP-0198: unset end_session_on_disconnect on resume/enable 2020-03-29 14:21:40 +02:00
Georg Lukas
621255027d Reset reconnect delay on manual reconnect, add delay event
This is just a hotfix workaround for an underlying problem. The
`_connect_routine` code is "blocking" (in an async way) for
`connect_loop_wait` seconds, so that a fresh-started manual reconnect
will be silenty delayed. This code does the following changes:

1. It moves the delay to before the DNS resolution (with the exponential
   back-off it might well be that the DNS records are changed while
   slixmpp is waiting).

2. It adds a new event `reconnect_delay` that gets passed the number of
   seconds it will delay before actually reconnecting

3. It resets the `connect_loop_wait` timer on a manual connect/reconnect
   call to fix the interactive experience.

A *proper fix* would replace the sleep in `_connect_routine` with a
properly timered re-invocation of it, but I don't understand enough of
asyncio for pulling off that magic, and this is actually a proper
improvement. Also I tested this and it works!
2020-03-29 14:21:05 +02:00
Maxime Buquet
efe316dc8c Merge branch 'fix-0198' into 'master'
XEP-0198: properly disable on disconnect, fix reconnect-loop

See merge request poezio/slixmpp!34
2020-03-28 22:11:52 +01:00
Maxime Buquet
e9a87a0b77 Merge branch 'master' into 'master'
reconnect: fix callback when not currently connected

See merge request poezio/slixmpp!32
2020-03-28 22:11:34 +01:00
Georg Lukas
85c9967b9c XEP-0198: fix race conditions on enable/resume
This code splits out the `enabled` property into `enabled_in` and
`enabled_out` to reflect that client and server enable 0198
asynchronously.

This also moves the actual enabling code into the stanza processing
logic, because apparently, `enable.send()` was just added into the end
of the send queue, but `enable` got enabled immediately, so that poezio
requested ACKs for whatever happened to be in the queue before.

Async is hard, let's go get fishing.
2020-03-28 21:49:18 +01:00
Georg Lukas
deb6d4f176 XEP-0198: properly disable on disconnect, fix reconnect-loop
When the connection is disconnected (but the session didn't "end",
because 0198 resumption is enabled), poezio will reconnect and try to
send an <r/> element because the 0198 plugin doesn't realize that SM
wasn't re-enabled yet.
2020-03-28 20:50:55 +01:00
Karthikeyan Singaravelan
7218bb4499 cElementTree has been deprecated since Python 3.3 and removed in Python 3.9. 2020-03-28 04:42:23 +00:00
Georg Lukas
d85efec7a2 reconnect: fix callback when not currently connected
The 'disconnected' event is normally fired from connection_lost(), which
is called by the connection code when the connection is lost after being
established. However, if the connection wasn't successfully established,
a manual /reconnect no-ops because it waits for the 'disconnected'
callback which never fires. This patch does two things:

1. Immediately fire a 'disconnected' event in disconnect() if there is
   no transport.

2. Register the 'disconnected' event handler in reconnect() *before* it
   can be fired.
2020-03-23 18:59:29 +01:00
mathieui
115c234527 Merge branch 'async-filters' into 'master'
Add async filters on the send process

See merge request poezio/slixmpp!24
2019-12-27 15:35:00 +01:00
mathieui
a0f5cb6e09 Put the queue exception at toplevel 2019-12-27 15:27:29 +01:00
mathieui
110bbf8afc Improve the send queue code a bit 2019-12-27 15:27:29 +01:00
mathieui
d97efa0bd8 add a separate place for slow ass filters 2019-12-27 15:27:29 +01:00
mathieui
672f1b28f6 raise Exception 2019-12-27 15:27:29 +01:00
mathieui
27d3ae958b Try/except around outbound stanza processing
to avoid killing the send loop when a filter has an error
2019-12-27 15:27:29 +01:00
mathieui
a32794ec35 Remove trailing whitespace 2019-12-27 15:27:28 +01:00
mathieui
aa11ba463e Skip 0323 because 2019-12-27 15:27:28 +01:00
mathieui
a83c00e933 Update test framework to work with new filters
(eewww)
2019-12-27 15:27:28 +01:00
mathieui
31f6ef6814 Run the send queue in a separate coroutine
To be able to run async stream filters
2019-12-27 15:27:25 +01:00
Maxime “pep” Buquet
9b3874b5df xep_0048: Ensure Conference jid is of type str when given a JID
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-11-11 14:00:19 +01:00
mathieui
0139fb291e Fix a bug in the 0202 plugin where a time result would send back a result
poezio bug #3499
2019-09-19 22:54:53 +02:00
mathieui
e58988484a Match the sender JID as well as the queryid in MAM results 2019-09-10 23:13:04 +02:00
mathieui
5d5e5cda19 Add typing/docstring in the MAM plugin 2019-09-10 22:44:46 +02:00
root
11f707987d Added amount parameter, so that limit on the msgs received per query can be changed. 2019-09-08 14:22:48 +02:00
Maxime “pep” Buquet
db13794e0f Revert "Remove a block of compatibility code"
This reverts commit 37bc1bb9b3.

Confusion confusion. Mathieui thought this was a sleekxmpp thing when
it's actually been added not so long ago.
2019-08-28 00:12:10 +02:00
mathieui
37bc1bb9b3 Remove a block of compatibility code
even if the user makes that mistake, it does not cause problems down the
line.
2019-08-26 12:00:38 +02:00
mathieui
9be30e5291 fix a typo in the invalidjid exception name case 2019-08-25 01:42:00 +02:00
Maxime Buquet
9fe20a4056 Merge branch 'origin-id' into 'master'
Implement Origin-id (XEP-0359)

See merge request poezio/slixmpp!21
2019-08-23 17:10:42 +02:00
Maxime “pep” Buquet
3253d34c0a basexmpp: Make origin-id opt-out
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-08-23 17:09:21 +02:00
Maxime “pep” Buquet
fef575ee1a Implement origin-id (XEP-0359)
This XEP is not implemented as a plugin but directly into Message.

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-08-23 17:09:21 +02:00
Maxime Buquet
540ff89427 Merge branch 'mam' into 'master'
Assign 'True' to 'before' tag if it's value is 'None'.

See merge request poezio/slixmpp!26
2019-08-23 16:37:12 +02:00
root
dd8ac8fc87 Assign True to the 'before' tag if it's value is None (eg:at the start no msg is there in the group, so no stanza-id) 2019-08-23 04:39:20 +05:30
Maxime Buquet
2249d878d1 Merge branch 'mam' into 'master'
Removed assigning 'reverse' value to the 'before' tag

See merge request poezio/slixmpp!25
2019-08-23 00:03:07 +02:00
root
89fa9dc1dd Removed before tag. (Code for setting it is already there) 2019-08-23 00:21:42 +05:30
root
d7729e8683 Removed assigning 'reverse' value to the 'before' tag (It's value is set in xep_0313 (mam.py file) and if not then by default it is takes as 'None'). 2019-08-23 00:15:56 +05:30
louiz’
d618f55dea Merge branch 'mam' into 'master'
Added <before> tag for querying messages before a stanza-id.

See merge request poezio/slixmpp!23
2019-08-13 09:54:43 +02:00
Madhur Garg
b0e688eb35 Added <before> tag for querying messages before a stanza-id. 2019-08-12 23:39:01 +05:30
Maxime “pep” Buquet
0e7176483b xep_0030: add docstring to get_info_from_domain
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-08-03 16:05:09 +02:00
Emmanuel Gil Peyrot
f35569a2c1 Remove the last instances of a block argument to iq.send().
Thanks Madhur Garg for spotting this in
027ce2434d7fd3cf4a286dd373cb761c0d114c66!
2019-08-01 22:36:59 +02:00
Link Mauve
bec6f7c8f3 Merge branch 'mam' into 'master'
Removed 'block' from set_preferences as it was giving a TypeError while sending the staza.

See merge request poezio/slixmpp!20
2019-08-01 22:30:58 +02:00
Madhur Garg
027ce2434d Removed 'block' from set_preferences as it was giving a TypeError while sending the staza. 2019-08-02 01:57:36 +05:30
Maxime Buquet
d57fbb57a2 Merge branch 'mam' into 'master'
Added a function in xep313 plugin to get current MAM preferences.

See merge request poezio/slixmpp!19
2019-07-22 22:05:05 +02:00
Madhur Garg
85cd7a9166 Added a function to get current MAM preferences. 2019-07-18 16:55:11 +05:30
Maxime “pep” Buquet
d50d996c68 xmlstream/stanzabase: remove unused interfaces and types attributes
These are already on each stanza, and were not applicable to all stanzas
anyway.

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-07-16 11:26:54 +02:00
mathieui
371ad20ca7 Do not add disco#info for occupantid, it’s a server thing 2019-07-14 13:25:22 +02:00
mathieui
5f49df6b56 Add the occupant id stanza elements 2019-07-14 13:13:59 +02:00
mathieui
b50bfb2f34 Initial commit for reactions protoxep 2019-07-13 19:44:32 +02:00
Maxime “pep” Buquet
b29bb30eb7 Make generated stanza id truly random
Fix long-standing security issues where stanza @id be predictable.

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-07-13 14:07:31 +02:00
Maxime “pep” Buquet
4435c81d77 xmlstream.disconnect: add compat behaviour, set wait to default 2.0 when True is passed. Update documentation
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-07-03 21:36:17 +02:00
Madhur Garg
2638ba2744 Added 'reverse' argument. 2019-07-03 14:18:33 +05:30
Madhur Garg
dbc9758311 Added 'reverse' parameter in mam and rsm plugins 2019-07-03 10:31:18 +05:30
Maxime “pep” Buquet
47968963b1 Revert part of previous commit. Return NotImplemented when object is not a valid JID
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-05-07 11:56:39 +01:00
Maxime “pep” Buquet
4e8800f954 jid: return not equal if value can't be converted to JID
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-05-07 10:08:46 +01:00
Maxime “pep” Buquet
40053518aa Merge remote-tracking branch 'origin/mr/13' 2019-04-27 12:59:23 +01:00
Maxime “pep” Buquet
1ee0f72ead xmlstream.disconnect: fix frenchism in docstring
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-24 23:35:33 +01:00
Maxime “pep” Buquet
4bb81228ae xmlstream.disconnect: typing hints
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-24 23:35:06 +01:00
Maxime “pep” Buquet
60a7a5b8df presence: Ensure <show/> value is valid when returned as presence @type value
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-24 20:29:54 +01:00
Maxime “pep” Buquet
946674f424 xep_0045: Ensure <show/> value is valid.
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-24 20:28:58 +01:00
Maxime “pep” Buquet
412a9169bd Fixes #3432. Allow execute to be used with the meaning of 'next'.
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-14 02:34:22 +01:00
Maxime “pep” Buquet
72b355de8c xep_0050: Fix indentation
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-14 02:19:58 +01:00
Maxime “pep” Buquet
af246dcfe1 slixmpp/jid: add types
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-07 23:14:44 +01:00
Maxime Buquet
9612e518fb Merge branch 'master' into 'master'
Communicate the reason for a disconnect to the application

See merge request poezio/slixmpp!12
2019-04-07 00:24:50 +02:00
Maxime “pep” Buquet
fde8264191 xep_0202: Fix plugin_init docstring
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-06 13:44:05 +01:00
Maxime “pep” Buquet
1cdc656208 Fixes poezio/poezio#3472: Don't remove TZ in 0202 utc tag
<utc/> MUST conform to 0082 dateTime profile and thus include a
timezone definition.

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-06 13:42:26 +01:00
Maxime “pep” Buquet
0042108a67 poezio/poezio#3472: Ensure tz is correctly set when offset is an int
Thanks lovetox!

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-04-03 23:16:37 +01:00
Georg Lukas
704161a285 mark end-of-stream as session-ending event 2019-03-26 15:16:52 +01:00
Georg Lukas
6b1b58a339 XEP-0199: use new 0-timeout reconnect() with reason 2019-03-26 12:09:43 +01:00
Georg Lukas
4f96e5fa75 Do not directly enqueue connect() as event handler, parameter mismatch 2019-03-26 12:09:43 +01:00
Georg Lukas
bcb90a653e Do not close stream on 0-timeout disconnect, allows 0198 resume 2019-03-26 11:02:28 +01:00
Georg Lukas
7e435b703d Propagate disconnect() reason into 'disconnected' event 2019-03-26 11:01:36 +01:00
Maxime “pep” Buquet
2dda6b80d4 Partially fix poezio/poezio#3452. Prevent groupchat_subject from triggered sent when body or thread are in the message.
0045 says:
> The subject is changed by sending a message of type "groupchat" to the
> <room@service>, where the <message/> MUST contain a <subject/> element that
> specifies the new subject but MUST NOT contain a <body/> element (or a
> <thread/> element). In accordance with the core definition of XMPP, other child
> elements are allowed (although the entity that receives them might ignore
> them).
>
> Note: A message with a <subject/> and a <body/> or a <subject/> and a <thread/>
> is a legitimate message, but it SHALL NOT be interpreted as a subject change.

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-03-23 17:06:07 +00:00
mathieui
5629e44710 Merge branch 'patch-1' into 'master'
Fix slixmpp.ClientXMPP.cancel_connection_attempt()

See merge request poezio/slixmpp!10
2019-03-15 00:38:35 +01:00
Maxime “pep” Buquet
6a06881d8b xep_0030: fix typo on 'remote' in get_info docstring
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-03-05 00:00:14 +00:00
Maxime “pep” Buquet
2b666eb1de Revert "Remove license from unused project"
This reverts commit fbab3ad214.
2019-02-24 12:16:06 +00:00
Emmanuel Gil Peyrot
400e7a3903 Remove SocksiPy license
I wrote a SOCKS5 implementation back in
9c5dd024b1, and removed SocksiPy from
slixmpp’s codebase at the same time.
2019-02-24 13:02:08 +01:00
Maxime “pep” Buquet
fbab3ad214 Remove license from unused project
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-02-24 12:00:16 +00:00
mathieui
628b357b06 Merge branch 'eme-add-method' into 'master'
xep_0380: Add add_eme method

See merge request poezio/slixmpp!11
2019-02-23 14:07:15 +01:00
Maxime “pep” Buquet
88260cc240 xep_0380: Add add_eme method
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-02-23 12:54:56 +00:00
Maxime “pep” Buquet
e9f2f503b8 xep_0380: Remove remove_handler call in plugin_end
Remove probable copy paste fail in EME plugin_end handler.

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-02-23 12:36:40 +00:00
ehendrix23
696a72247b Fix slixmpp.ClientXMPP.cancel_connection_attempt() 2019-02-22 00:41:02 +01:00
Maxime “pep” Buquet
05d76e4b1d Change more URLs to lab.louiz.org
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-02-12 10:34:57 +00:00
Maxime “pep” Buquet
d52d4fbbbe Update project URLs to lab.louiz.org
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-02-12 10:26:04 +00:00
Maxime “pep” Buquet
e53c0fcb30 README: Add pep as a contributor
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-02-10 14:18:28 +00:00
Maxime “pep” Buquet
97d68c5196 setup.py: GTalk is no more
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2019-02-10 14:18:28 +00:00
mathieui
b42fafabb4 Make the cache encode and decode not crash if something goes wrong 2019-02-02 17:42:24 +01:00
mathieui
3a44ec8f15 Add tests for the cache api 2019-02-02 17:32:10 +01:00
mathieui
93f385562f Add a "remove" action on the cache API 2019-02-02 17:31:48 +01:00
mathieui
9cab02438b Fix XEP-0335 2019-02-02 15:41:55 +01:00
Emmanuel Gil Peyrot
74ed50e626 Set @id by default on outgoing messages and presences.
Respects RFC6120 §8.1.3’s RECOMMENDED.
2019-01-31 16:46:51 +01:00
mathieui
9d378c611c Release 1.4.2 2019-01-31 14:50:26 +01:00
Link Mauve
d85d8f4479 Merge branch 'xep-0335' into 'master'
Add xep_0335: JSON Containers

See merge request poezio/slixmpp!5
2019-01-22 19:28:38 +01:00
Emmanuel Gil Peyrot
fb75f7cda9 Stop requesting avatar without the intervention of the client. 2019-01-22 15:12:00 +01:00
Emmanuel Gil Peyrot
41419a2161 Fix authenticating on a non-TLS socket.
This was broken since c1562b76b2.
2019-01-21 01:02:06 +01:00
Emmanuel Gil Peyrot
7cd73b594e XEP-0223: Fix default access_model, it MUST be whitelist. 2019-01-17 12:08:51 +01:00
Emmanuel Gil Peyrot
15c6b775ff Simplify the non-CDATA path of tostring.escape. 2019-01-09 15:03:05 +01:00
Emmanuel Gil Peyrot
4b482477e2 Split ns only once in fix_ns(). 2019-01-09 14:57:39 +01:00
Emmanuel Gil Peyrot
f7e4caadfe Split tag and attrib only once in tostring(). 2019-01-09 14:55:27 +01:00
Emmanuel Gil Peyrot
5f25b0b6a0 Add a default timeout to iq.send().
This fixes a leak of MatchIDSender in handlers, making it more and more
expensive to match stanzas as more iqs have been sent.
2019-01-09 14:20:31 +01:00
Mateusz Piotrowski
d228bc42ea Mention that GnuPG is required for tests 2018-12-27 17:21:12 +01:00
mathieui
ecdc44a601 Merge branch 'master' into 'master'
Decode bytes in GSSAPI handling, as expected by the kerberos module API.

See merge request poezio/slixmpp!8
2018-12-27 16:55:47 +01:00
Emmanuel Gil Peyrot
33370e42f1 XEP-0363: Use a specific exception for HTTP errors 2018-11-20 07:44:09 +01:00
Florian Klien
4699861925 catch http upload errors on upload 2018-11-20 07:34:56 +01:00
Jelmer Vernooij
2d228bdb56 Decode bytes in GSSAPI handling, as expected by the kerberos module API. 2018-10-30 22:29:20 +00:00
mathieui
570e653ac2 Release slixmpp 1.4.1 2018-10-28 14:15:51 +01:00
mathieui
282a481059 Merge branch 'setup_dependency_fix' into 'master'
added aiohttp dependency

See merge request poezio/slixmpp!6
2018-10-28 14:08:26 +01:00
Florian Klien
f386db380b docs: auto-set copyright year to current year 2018-10-28 10:48:23 +01:00
Florian Klien
7b87d98fff auto set version of Slixmpp in Docs
getting version of slixmpp from source tree for documentation
2018-10-28 10:48:23 +01:00
Florian Klien
8779d40602 typo 2018-10-27 23:38:09 +02:00
Emmanuel Gil Peyrot
f0b21c42d5 examples: Add the possibility to use another HTTP File Upload domain. 2018-10-27 23:25:59 +02:00
Emmanuel Gil Peyrot
e241d4e3c7 XEP-0030: Don’t call the timeout_callback on each domain which doesn’t reply to disco#info. 2018-10-27 23:21:27 +02:00
Emmanuel Gil Peyrot
bd22a41a78 XEP-0363: Also check for disco#info’s feature instead of just the identity. 2018-10-27 23:14:39 +02:00
Emmanuel Gil Peyrot
a29a29227a XEP-0363: Add a domain argument to discover an upload service on a specific domain. 2018-10-27 22:51:04 +02:00
Florian Klien
d4d542b741 fixing uncaught async exceptions due to missing await
fixes uncaught exceptions in the event loop.
passing timeout and timeout_callback through.
2018-10-27 22:51:04 +02:00
Florian Klien
dc4936a6d3 added aoihttp dependency
slixmpp uses aiohttp in the XEP_0363 plugin.
2018-10-22 12:41:20 +02:00
Florian Klien
897610d819 fix: failUnlessEqual -> assertEqual 2018-10-15 14:59:23 +02:00
Florian Klien
d33366badd fixing deprecation warnings for pytest 2018-10-15 14:59:23 +02:00
mathieui
809c500002 Add the loop parameters at places where it has been forgotten 2018-10-09 12:34:56 +02:00
Emmanuel Gil Peyrot
dda4e18b81 examples: Remove unused asyncio imports. 2018-10-09 10:57:19 +02:00
Emmanuel Gil Peyrot
8c09d932c8 stanzabase: Remove python2 legacy. 2018-10-03 14:56:07 +02:00
Maxime “pep” Buquet
31f5e84671 Add xep_0335: JSON Containers
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2018-09-16 22:13:41 +01:00
louiz’
ad0dc33df9 Trigger poezio’s build if this ours succeeded 2018-08-22 23:19:47 +02:00
Emmanuel Gil Peyrot
7c3b3827b4 jid: Make property aliases proper aliases. 2018-08-20 00:23:21 +01:00
Emmanuel Gil Peyrot
9f6fa65139 examples, tests: Replace all @asyncio.coroutines with proper async functions. 2018-08-19 17:47:26 +01:00
mathieui
35fa33e3c2 Release slixmpp 1.4.0 2018-08-12 21:11:21 +02:00
mathieui
86a2f280d2 Document that slixmpp is now 3.5+ only 2018-08-08 23:42:22 +02:00
mathieui
490f15b8fc Fix compatibility with python 3.5 and 3.6
which do not have loop.start_tls and require the old ssl implementation.
2018-08-08 23:35:33 +02:00
Emmanuel Gil Peyrot
62661ee04f xep_0092: Return <service-unavailable/> instead. Fixes #3415. 2018-08-08 16:52:40 +02:00
Emmanuel Gil Peyrot
37d1f2a6b0 xep_0092: Send a <forbidden/> error if we don’t want to send our version. (thanks lovetox!) 2018-08-08 16:49:16 +02:00
mathieui
20107ad516 features/starttls: handle the case where the socket is an sslobject
Thanks pep.
2018-08-07 23:30:41 +02:00
mathieui
7738a01311 Fix TLS with python 3.7
Use the "new" sslproto API instead of the deprecated TLS API.
Also remove the unused "socket" parameter in XMLStream.__init__.
2018-08-07 23:20:38 +02:00
mathieui
a9abed6151 xep-0054: XMPP clients should not reply to vcard "get" requests 2018-08-07 21:30:13 +02:00
Emmanuel Gil Peyrot
0f690d4005 tests: Fix the XEP-0323 stream test, broken since 59d4420739.
Thanks debacle!
2018-08-02 21:27:19 +02:00
Emmanuel Gil Peyrot
59d4420739 XEP-0323: Display the requested time in addition to the current time on error. 2018-07-29 10:37:28 +02:00
Emmanuel Gil Peyrot
a88f317bbf XEP-0009: Fix invalid function name under Python 3.7. 2018-07-24 18:21:03 +02:00
Link Mauve
2fc2a88970 Merge branch 'factor_find_identities' into 'master'
Factor find identities

See merge request poezio/slixmpp!2
2018-07-22 12:49:37 +02:00
Emmanuel Gil Peyrot
c55e9279ac Fix missing async def in function. 2018-07-02 14:33:21 +02:00
Emmanuel Gil Peyrot
3502480384 Switch from @asyncio.coroutine to async def everywhere. 2018-07-01 18:46:33 +02:00
Maxime “pep” Buquet
caae713dd6 xep_0030: rename find_identities; return all domain infos and let caller filter itself
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2018-07-01 01:36:26 +01:00
Maxime “pep” Buquet
df0198abfe xep_0030: Add callback parameter to find_identities
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2018-07-01 01:32:00 +01:00
Maxime “pep” Buquet
c20f4bf5fa xep_0030: Add cached parameter to find_identities, defaults to True
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2018-07-01 01:29:31 +01:00
Maxime “pep” Buquet
9740e93aeb xep_0030: Pass kwargs down in find_identities
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2018-07-01 01:29:31 +01:00
Maxime “pep” Buquet
e7872aaa29 xep_0030: Use self directly as we're already in disco
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2018-07-01 01:29:31 +01:00
Maxime “pep” Buquet
037706552c Factor out fetching of identities in xep_0363 to xep_0030
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2018-07-01 01:29:31 +01:00
Maxime “pep” Buquet
b881c6729b xep_0363: Remove unused parameters to find_upload_services
Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
2018-07-01 01:29:31 +01:00
Emmanuel Gil Peyrot
66909aafb3 XEP-0153: Prevent a panic when the BINVAL is invalid. 2018-06-23 14:34:24 +02:00
louiz’
cdfb5d56fc apt install gpg before running the ci tests 2018-06-10 21:40:56 +02:00
Emmanuel Gil Peyrot
d146ce9fb6 examples: Display only the form, and not the entire stanza. 2018-05-13 21:26:47 +02:00
Emmanuel Gil Peyrot
cb59d60034 examples: Display the actual cause for a failed command. 2018-05-13 21:21:06 +02:00
Emmanuel Gil Peyrot
1d9fe3553e examples: Use the existing get_node_config function. 2018-05-13 21:13:22 +02:00
Emmanuel Gil Peyrot
fe66c022ad Revert "XEP-0060: Add support for node configuration."
This reverts commit dd7f67d10d.
2018-05-13 21:12:46 +02:00
Emmanuel Gil Peyrot
92ea131721 examples: Add support for node configuration to pubsub_client. 2018-05-13 20:57:48 +02:00
Emmanuel Gil Peyrot
dd7f67d10d XEP-0060: Add support for node configuration. 2018-05-13 20:47:21 +02:00
Emmanuel Gil Peyrot
c1562b76b2 slixmpp is Python 3.4.4+, remove check for channel binding. 2018-03-31 02:22:53 +02:00
Emmanuel Gil Peyrot
32839f5252 util.cache: Let the user select the bare JID or not. 2018-03-31 00:44:53 +02:00
Emmanuel Gil Peyrot
80b7cf6ff8 util.cache: Support None for encode and decode. 2018-03-31 00:44:23 +02:00
Emmanuel Gil Peyrot
128cc2eeb4 XEP-0115: Use the new cache system. 2018-03-31 00:25:28 +02:00
Emmanuel Gil Peyrot
037912ee89 util.cache: New module handling both in-memory and on-file system caching. 2018-03-31 00:24:21 +02:00
Emmanuel Gil Peyrot
769bc6d3bf session: Also fire the session_start event. 2018-03-14 19:39:01 +01:00
Emmanuel Gil Peyrot
084d6cb5d9 session: Don’t bind if it is optional.
See https://tools.ietf.org/html/draft-cridland-xmpp-session-01
2018-03-14 18:54:17 +01:00
Emmanuel Gil Peyrot
5184713356 Rearm an iq callback if it was addressed to ourself. 2018-03-14 17:37:55 +01:00
Emmanuel Gil Peyrot
2f1225bad3 Carry the node attribute to the disco#info result.
Fixes #3323.
2018-03-14 16:25:21 +01:00
Emmanuel Gil Peyrot
841f5a5a5b xep_0363: Only send the basename() of the filename to the server. 2018-03-11 19:40:15 +01:00
Emmanuel Gil Peyrot
0c6de5e972 xep_0363: Simplify Content-Type guessing. 2018-03-11 19:39:51 +01:00
Emmanuel Gil Peyrot
81dc61c55c xep_0363: Fix max_file_size variable name. 2018-03-11 19:39:30 +01:00
Emmanuel Gil Peyrot
bd63b1ce70 Simplify usage of HTTP File Upload plugin.
This makes it usable only on Python 3.5, as documented.
2018-03-08 14:29:07 +01:00
Emmanuel Gil Peyrot
29faf114a7 Add max-file-size support to HTTP File Upload example. 2018-03-08 12:11:26 +01:00
Emmanuel Gil Peyrot
94ea8151d4 Add an HTTP File Upload example. 2018-03-08 03:39:23 +01:00
Emmanuel Gil Peyrot
66500ef5fb Add an HTTP File Upload plugin. 2018-03-08 03:38:59 +01:00
mathieui
979396bb1e asyncio.async has been scheduled for removal for a long time now
move to asyncio.ensure_future
2018-02-11 19:25:38 +01:00
mathieui
e177726387 Fix usage of the 0004 plugin interface
form['fields'] is an ordered list of fields while most plugins expect a
dict there. Fixes, among other things, a caps bug.
2018-02-11 16:42:59 +01:00
Emmanuel Gil Peyrot
20e88fda50 Fix typos, thanks codespell! 2018-01-10 02:18:07 +01:00
Emmanuel Gil Peyrot
f252be9b6d XEP-0115: Fix typo. 2018-01-05 18:03:42 +01:00
Emmanuel Gil Peyrot
ee98159586 Test all known python versions in travais 2017-12-27 15:49:52 +01:00
Emmanuel Gil Peyrot
c6443af29a stringprep: Make pure-Python punycode() return bytes.
Fixes #3366.
2017-12-27 15:48:42 +01:00
mathieui
d73f56a7af Release slixmpp 1.3.0 2017-11-28 20:16:08 +01:00
Emmanuel Gil Peyrot
7c7f4308c5 Add a Markup plugin. 2017-11-23 12:18:01 +00:00
mathieui
eab8c265f4 Record the current connection attempt in a future and allow cancellation
It does not make sense to have competing connection attempts, as the
XMLStream class is not designed for this. On slow and unpredictable
networks, it means we could have two c2s connections opened, leading to
mayhem.
2017-11-23 00:00:37 +01:00
Emmanuel Gil Peyrot
80b9cd43b1 MAM example: Also display the timestamp. 2017-10-24 10:54:53 +01:00
Emmanuel Gil Peyrot
af1f9e08ad Clean up the MAM example a bit. 2017-10-24 10:47:42 +01:00
Emmanuel Gil Peyrot
e3fd0af9c8 xep_0054: Fix parsing BINVAL element. 2017-10-08 15:42:48 +01:00
mathieui
27e23672c1 Update the MAM plugin for asyncio & new namespace
And add an example
2017-09-24 17:43:06 +02:00
mathieui
b38e229359 Update RSM for asyncio
- Use an async iterator
- Add a "recv_interface" parameter in order to differenciate the stanza
   we send from the stanza we receive (required for MAM)
- Add a pre_cb to run before sending the query stanza
- Add a post_cb to run after receiving the result stanza
2017-07-21 15:01:13 +02:00
Emmanuel Gil Peyrot
9a563f1425 XEP-0030: Optimise add_node usage a bit. 2017-07-17 22:46:48 +01:00
Emmanuel Gil Peyrot
8b6f5953a7 XEP-0319: Use the correct timezone.
This fixes a specification violation, XEP-0082 says that a date MUST
have a timezone, but we were sending the *local* time without any
timezone information.
2017-07-17 22:20:30 +01:00
Emmanuel Gil Peyrot
2d2a80c73d xmlstream: Remove pygments dumping.
It’s slow and makes the debug logs difficult to parse.
2017-07-17 21:17:02 +01:00
Mathias Ertl
4dfdd5d8e3 always define ssl_context 2017-05-24 13:18:22 +02:00
Mathias Ertl
1994ed3025 pass SSL context to TLS connections 2017-05-24 11:31:13 +02:00
Mathias Ertl
aaa45846d3 add function to explicitly get the ssl context 2017-05-24 11:31:13 +02:00
louiz’
d7ffcb54eb Merge remote-tracking branch 'samwhited/sslsocket_workaround' 2017-05-16 17:24:46 +02:00
Tom Wambold
c33749e57a Fixes port being set to 0 when connecting via hostname.
This seems to be the same issue as:

  https://dev.louiz.org/issues/3164

Using their suggested fix, if the DNS lookup doesn't return a port, use
the one passed in instead.
2017-05-08 15:58:28 -04:00
Emmanuel Gil Peyrot
e4107d8b4d sasl: Merge two bytes instead of concatenating them at runtime. 2017-04-28 21:26:03 +01:00
mathieui
da5cb72d3a Add XMPP classifier to setup.py 2017-04-10 02:24:14 +02:00
Emmanuel Gil Peyrot
c372bd5168 xmlstream: Warn when the parser is None when data is received. 2017-02-16 11:27:36 +00:00
mathieui
cabf623131 Fix the http over xmpp example 2017-02-14 01:04:38 +01:00
mathieui
ffc240d5b6 Fix the gtalk example 2017-02-14 01:04:27 +01:00
mathieui
cc4522d9cd Fix custom stanza examples 2017-02-14 01:00:41 +01:00
mathieui
5bf69dca76 Return a Future on clientxmpp.get_roster() 2017-02-14 00:46:36 +01:00
Emmanuel Gil Peyrot
59dad12820 XEP-0300: Workaround for Python 3.5 or below. 2017-02-11 23:30:43 +00:00
Emmanuel Gil Peyrot
007c836296 XEP-0300: Add rudimentary tests. 2017-02-11 04:02:44 +00:00
Emmanuel Gil Peyrot
3721bf9f6b Implement XEP-0300 (Use of Cryptographic Hash Functions in XMPP)
This is used to provide hash agility support and let other XEPs select
which hash function they support.
2017-02-11 04:02:20 +00:00
Cédric 'dek' Laudrel
802949eba8 fix small typo in README 2017-02-10 00:08:40 +01:00
mathieui
24f35e433f slixmpp 1.2.4 release 2017-01-30 23:02:45 +01:00
mathieui
22664ee7b8 Fix carbons 2017-01-28 00:02:27 +01:00
Clint Olson
6476cfcde5 Remove unused import caught by Codacy. 2017-01-23 23:58:53 -08:00
Clint Olson
5bb347e884 Fix partially-merged Google plugin from acc52fd935. 2017-01-23 23:51:59 -08:00
Emmanuel Gil Peyrot
eb1251b919 Fix a typo in the title of the MUC documentation. 2017-01-08 17:38:11 +00:00
Emmanuel Gil Peyrot
820144c40c Add missing asyncio.coroutine decorators. 2016-12-30 13:41:15 +01:00
Emmanuel Gil Peyrot
6034df0a78 Check for XML parsing errors and disconnect in that case. 2016-12-29 18:59:09 +01:00
Emmanuel Gil Peyrot
df4012e66d XMLStream: Break a long line to make it more readable. 2016-12-29 18:41:09 +01:00
Emmanuel Gil Peyrot
c372f3071a Examples: Use argparse for http_over_xmpp. 2016-12-29 18:34:37 +01:00
Emmanuel Gil Peyrot
829c8b27b6 Test more things before trying to build our stringprep module. 2016-12-25 13:28:51 +01:00
mathieui
fb3ac78bf9 slixmpp 1.2.3 2016-12-07 21:47:54 +01:00
mathieui
ffd9436e5c Fix roster push origin detection and tests 2016-12-07 19:06:25 +01:00
louiz’
bbb1344d79 Add very basic gitlab-ci.yml file 2016-12-05 00:13:13 +01:00
Emmanuel Gil Peyrot
457785b286 XEP-0380: Add a helper to test for the presence of an EME tag. 2016-11-26 16:41:48 +00:00
Emmanuel Gil Peyrot
4847f834bd Add a plugin for XEP-0380: Explicit Message Encryption. 2016-11-26 16:29:19 +00:00
mathieui
53191ff1cf slixmpp 1.2.2
Fix CVE-2015-8688, and a few bugfixes.
2016-11-21 21:46:02 +01:00
mathieui
ffdb6ffd69 Check origin of roster pushes
slixmpp is vulnerable to roster push attacks as described by Daniel
Gultsch at https://gultsch.de/gajim_roster_push_and_message_interception.html.

(CVE-2015-8688)
2016-11-21 21:42:51 +01:00
Emmanuel Gil Peyrot
7560db856b stringprep_profiles: Emit a correct StringPrepError on query + unassigned. 2016-10-27 06:44:38 +01:00
Emmanuel Gil Peyrot
63d245ac48 SASL: Fix traceback on non-hashing mechanism using channel binding. 2016-10-27 06:26:39 +01:00
Emmanuel Gil Peyrot
7ddd37be29 XEP-0323: Fix wrong import. 2016-10-27 06:23:08 +01:00
Emmanuel Gil Peyrot
a4d3a4a25e XEP-0313: Add missing setter argument. 2016-10-27 06:22:50 +01:00
mathieui
58bd07628b Add missing parameters in XEP-0222 and XEP-0223 2016-10-27 00:21:01 +02:00
mathieui
3569038493 XEP-0009: fix a traceback on recipient unavailable
(probably a past typo)
2016-10-27 00:18:43 +02:00
mathieui
20c4ff823a Add missing JID import in XEP-0079 and 0258 2016-10-27 00:17:29 +02:00
mathieui
8a7448a5a1 Add missing imports in XEP-0333 2016-10-27 00:15:25 +02:00
mathieui
d23d8f901e Fix a traceback on XEP-0221 del uri['value']
(typo)
2016-10-27 00:11:27 +02:00
Emmanuel Gil Peyrot
391f12eeab Transform an if into an elif in cert parsing. 2016-10-23 14:15:02 +01:00
Emmanuel Gil Peyrot
d008988843 Manual cleanup of the remaining set([…]) and set((…)). 2016-10-22 13:37:46 +01:00
Emmanuel Gil Peyrot
dcacc7d7d5 sed -i 's/set(\[\(.*\)\])$/{\1}/g' **/*.py 2016-10-22 13:21:44 +01:00
Emmanuel Gil Peyrot
c4285961df sed -i 's/set((\(.*\)))$/{\1}/g' **/*.py 2016-10-22 13:21:42 +01:00
Emmanuel Gil Peyrot
1038f656eb sed -i 's/set((\(.*\),))$/{\1}/g' **/*.py 2016-10-22 13:21:41 +01:00
Sam Whited
8b06aa1146 Fix fetching the SSL socket for Python 3.4 and 3.5 2016-10-06 13:00:17 -05:00
Emmanuel Gil Peyrot
3c7236fe73 setup.py: Check for libidn before trying to use Cython. 2016-10-05 20:28:11 +01:00
mathieui
36824379c3 slixmpp 1.2.1
Fix a few bugs along with the testsuite, and remove the asyncio loop
monkeypatch hack.
2016-10-05 20:32:32 +02:00
mathieui
a0a37c19ff Remove monkeypatching hack on the event loop
This allowed us to schedule events in-order later in the event loop, but
was detrimental to using other event loops and debugging.
2016-10-05 20:19:07 +02:00
mathieui
1b5fe57a5e Fix XEP-0060 tests 2016-10-04 21:21:55 +02:00
mathieui
5da31db0c7 Fix stanza accessors case in tests
They were using deprecated (and-removed) style.
2016-10-04 21:15:01 +02:00
mathieui
f8cea760b6 Fix the gmail_notify plugin 2016-10-04 21:10:10 +02:00
mathieui
5ef01ecdd1 Fix XEP-0033
Re-add relevant stanza methods, broken in 7cd1cf32ae
2016-10-04 19:47:11 +02:00
mathieui
62aafe0ee7 Attrib property has been removed 2016-10-04 19:43:45 +02:00
mathieui
cf3f36ac52 Set unset part of a JID to empty string instead of None
it breaks assumptions on the type of the value
2016-10-04 19:42:05 +02:00
mathieui
b88d2ecd77 Add more checks in the XEP-0060 stanza building
Try to not append slixmpp stanzas to ElementTree objects.
2016-10-04 19:31:49 +02:00
mathieui
e691850a2b Fix XEP-0128
Broken since 125336aeee due to unforeseen consequences of a variable
removal.
2016-10-04 19:26:03 +02:00
mathieui
d4bff8dee6 Fix XEP-0009
Broken since 3a9b45e4f because of an overzealous cleanup.
2016-10-04 19:23:21 +02:00
mathieui
187c350805 Update for slixmpp 1.2 2016-10-02 17:36:14 +02:00
mathieui
96d1c26f90 Add a fallback if the lang we want is not available
Previously, trying to get a text node with a lang which is different
from the one we specified would return nothing, which means e.g. a
message would be ignored because its body is of lang 'fr' when we setup
slixmpp to prefer 'en'. We want to return something when there is an
available, valid content in a different language.
2016-10-02 17:12:47 +02:00
mathieui
46a90749f8 Fix uses of super() in the codebase
Fix #3165, we don’t need to use the long form to get the superobject in
our supported python versions.
2016-09-30 21:25:36 +02:00
mathieui
0c63a4bbda Fix #3226 (unicity of scheduled event names)
Thanks tchiroux for raising the issue and providing the fix as well.
2016-09-30 20:59:31 +02:00
mathieui
e4696e0471 Merge branch 'doc_fixes' of https://github.com/SamWhited/slixmpp 2016-09-30 20:53:36 +02:00
Sam Whited
8217dc5239 Minor documentation fixes 2016-09-30 13:49:04 -05:00
mathieui
2586abc0d3 Fix xep-0050 stanza
broken in 3a9b45e4f2
2016-09-20 20:51:21 +02:00
Emmanuel Gil Peyrot
28f84ab3d9 ElementBase: Remove support for TitleCase methods.
This gains about 1/8th of the time spent in __getitem__.
2016-09-21 01:31:53 +09:00
Emmanuel Gil Peyrot
813b45aded XEP-0045: Remove support for old-style {get,set,del}TitleCase methods. 2016-09-21 01:28:24 +09:00
Emmanuel Gil Peyrot
3a9b45e4f2 ElementBase: Remove deprecated find() and findall() methods. 2016-09-20 16:45:29 +09:00
Emmanuel Gil Peyrot
b8e091233e XEP-0004: Remove deprecated getXML() and fromXML() methods. 2016-09-20 16:34:48 +09:00
Emmanuel Gil Peyrot
0edeefd977 BaseXMPP: Stop automatically enabling UserNick, and remove deprecated alias module. 2016-09-20 16:23:02 +09:00
Emmanuel Gil Peyrot
6ba53cf1ff ElementBase: Remove attrib interface. 2016-09-20 16:23:02 +09:00
Emmanuel Gil Peyrot
d7758eb7f4 ElementBase: Remove subitem interface. 2016-09-20 16:23:02 +09:00
Emmanuel Gil Peyrot
125336aeee Remove locking from static disco. 2016-09-20 16:23:02 +09:00
Emmanuel Gil Peyrot
7cd1cf32ae Various XEPs: Remove deprecated aliases. 2016-09-20 16:23:02 +09:00
Emmanuel Gil Peyrot
d099e353a4 Implement XEP-0333: Chat Markers. 2016-08-26 22:42:24 +01:00
Emmanuel Gil Peyrot
1e4a301c6e Replace _format_jid with a JID method updating both bare and full at the same time. 2016-08-26 22:25:58 +01:00
mathieui
f53b12d227 Fix the MUC address in contributing.rst 2016-08-23 23:10:17 +02:00
Dan Sully
e2562dcccf Make session_bind_event awaitable 2016-08-23 23:05:22 +02:00
louiz’
7b69ae3738 Add a contributing file 2016-08-24 00:33:07 +02:00
Emmanuel Gil Peyrot
ab6df235d7 Pre-compute JID bare and full forms, and store that in each JID.
This wins about 4s over a 54s real-world benchmark.
2016-08-22 23:43:16 +01:00
mathieui
52cd8f4b22 Don’t trigger presence events on MUC presence
Specifically, previously, each MUC would be added as a roster item, and
then each join presence would be counted as a resource of that item,
triggering 1 to 5 events and more backend logic in slixmpp.

As a result, joining big rooms is tremendously slow, (JID() calls,
event() calls, __getitem__ calls for nothing), and takes RAM (a quick
tracemalloc tells me around 1 MiB for 3500 participants, i.e. 2 big IRC
rooms). Those resources may not necessarily be cleaned properly, leading
to memory leaks on long-term usage.

This is a micro-optimization that adds an attribute to roster items so
that MUC room events can be ignored safely while not affecting common
roster usage.
2016-08-22 01:29:07 +02:00
Emmanuel Gil Peyrot
e28318c271 Micro-optimise _format_jid. 2016-08-21 20:26:51 +01:00
mathieui
39ee833c29 Improve XEP-0070 and examples 2016-08-19 23:48:37 +02:00
Emmanuel Gil Peyrot
9019e2bc71 Initial work on XEP_0070, plugin and examples 2016-08-19 23:48:29 +02:00
louiz’
9208bf5bf1 Merge remote-tracking branch 'zejn/master' 2016-08-19 11:18:27 +02:00
Emmanuel Gil Peyrot
f0f1698e46 ElementBase: micro-optimise __getitem__, hands down the most often called function
This makes it go down from 8.767s to 7.960s in a random benchmark.

Remove unnecessary assignations, don’t create an OrderedDict from a
dict to then convert it to a dict again, only obtain the get_method2
name if get_method wasn’t present.

get_method2 (the title-case one) takes about 1/8th of the total time
spent in this function, we should eliminate it as soon as possible.
2016-08-17 00:46:56 +01:00
Gasper Zejn
eccd7f1c98 Provide domain name to loop.create_connection if using SSL. 2016-08-12 15:32:42 +02:00
Emmanuel Gil Peyrot
2587d82af8 Make util.XOR about ten times faster by calling bytes only once. 2016-07-30 00:14:54 +01:00
mathieui
7ea121b115 Don’t swallow presence exceptions abritrarily 2016-06-28 20:58:47 +02:00
mathieui
bb81fbbdfc Implement XEP-0256 (last activity in presence)
mostly useless, but allows to use LastActivity stanzas inside Presence
stanzas as well.
2016-06-05 02:04:52 +02:00
mathieui
1a00a08b7d Make XEP-0186 return futures as well
Improving the api if the developer wants to wait on them.
2016-06-05 00:19:24 +02:00
mathieui
90ea2a3411 Implement XEP-0352 (client state indication) 2016-06-04 22:59:23 +02:00
mathieui
8fc6814b6d Update XEP-0198 for asyncio 2016-06-04 20:51:59 +02:00
mathieui
ffced0ed9a Add a xep-0334 plugin 2016-06-04 19:34:12 +02:00
mathieui
e7248d9af9 Fix the Waiter handler for asyncio 2016-05-28 20:53:41 +02:00
mathieui
6b1a04f59d Fix xep-0199
The keepalive ping was not working, and and ping() was tracebacking due
to a wrong parameter.
2016-05-28 15:13:33 +02:00
mathieui
4905407092 Fix the ordering of stream features
since iq.send is non-blocking, some features handlers could end up
being executed before others were set, leading to issues. Adding yield
from where it’s necessary fixes that.
2016-05-28 14:46:39 +02:00
louiz’
bd6ec10939 Add some credits 2016-03-15 09:35:36 +01:00
Sam Whited
e15e6735f1 The XEP-0198 plugin exists now; fix the docs 2016-03-14 23:59:01 +01:00
mathieui
67afd6a462 Fix #3166 (broken link) 2016-02-03 22:43:47 +01:00
mathieui
2e2b97c53b Merge branch 'xep_0012_fix' of https://github.com/misuzu/slixmpp 2016-01-21 23:22:28 +01:00
Tsukasa Hiiragi
a35df7fe1f Fixed NameError in start_uptime 2016-01-21 14:59:03 +02:00
Krzysztof Kotlenga
fbc8562779 Remove dead code
See 5c769632e8.
2015-12-15 19:44:29 +01:00
mathieui
b549db959a Update version to 1.1 2015-10-02 19:35:29 +02:00
mathieui
d5188ac68a Mention the build of cython modules in the README 2015-10-02 19:22:26 +02:00
mathieui
ada9444bf8 Merge branch 'sleek-merge' 2015-10-02 19:07:45 +02:00
mathieui
acc52fd935 Merge branch 'develop' of https://github.com/fritzy/SleekXMPP into sleek-merge
Conflicts:
	README.rst
	examples/IoT_TestDevice.py
	examples/disco_browser.py
	setup.py
	sleekxmpp/jid.py
	sleekxmpp/plugins/google/auth/stanza.py
	sleekxmpp/plugins/google/gmail/notifications.py
	sleekxmpp/plugins/google/nosave/stanza.py
	sleekxmpp/plugins/google/settings/settings.py
	sleekxmpp/thirdparty/__init__.py
	sleekxmpp/thirdparty/socks.py
	sleekxmpp/thirdparty/statemachine.py
	sleekxmpp/util/__init__.py
	sleekxmpp/xmlstream/xmlstream.py
	slixmpp/basexmpp.py
	slixmpp/plugins/xep_0004/stanza/form.py
	slixmpp/plugins/xep_0009/rpc.py
	slixmpp/plugins/xep_0050/adhoc.py
	slixmpp/plugins/xep_0065/proxy.py
	slixmpp/plugins/xep_0084/stanza.py
	slixmpp/plugins/xep_0202/time.py
	slixmpp/plugins/xep_0323/sensordata.py
	slixmpp/plugins/xep_0325/control.py
	slixmpp/plugins/xep_0325/stanza/control.py
	slixmpp/roster/single.py
	slixmpp/stanza/atom.py
	slixmpp/stanza/rootstanza.py
	slixmpp/test/slixtest.py
	slixmpp/util/sasl/mechanisms.py
	slixmpp/version.py
	slixmpp/xmlstream/stanzabase.py
	tests/test_stanza_xep_0323.py
	tests/test_stanza_xep_0325.py
	tests/test_stream_xep_0323.py
	tests/test_stream_xep_0325.py
2015-10-02 19:00:19 +02:00
mathieui
1100ff1feb Reset the DNS answers after a connection is made succesfully 2015-09-25 19:34:04 +02:00
mathieui
c17fc3a869 Fix IPv6 resolving with aiodns 1.0 2015-09-24 19:38:53 +02:00
mathieui
4dba697075 Fix support for python 3.4 <= 3.4.2
asyncio module is provisional, which means it gets updated everytime
2015-09-23 23:23:02 +02:00
mathieui
e42d651d7e Fix connecting to a custom host/port 2015-09-19 15:27:12 +02:00
Mike Taylor
4305eddb4f Merge pull request #397 from rerobins/xep_0050_updates
Xep 0050 updates
2015-09-18 16:18:41 -04:00
Robert Robinson
c2dc44cfd1 Merge branch 'develop' into xep_0050_updates
# Conflicts:
#	tests/test_stream_xep_0050.py
2015-09-18 13:35:28 -06:00
Robert Robinson
5fc14de32e Merge pull request #3 from fritzy/develop
Merge to fritzy_master
2015-09-18 13:30:30 -06:00
Mike Taylor
d245558fd5 Merge pull request #396 from rerobins/add_xep_0122
XEP_0122: Add support for form validation
2015-09-18 15:15:27 -04:00
Mike Taylor
9d45370e8a Merge pull request #393 from aalba6675/fix/time
Only send time if Iq type is get.
2015-09-18 15:15:01 -04:00
Mike Taylor
cc1cc61d36 Merge pull request #392 from aalba6675/fix/tel_number
Do not overwrite telephone numbers
2015-09-18 15:14:35 -04:00
Mike Taylor
c6740a4908 Merge pull request #389 from alexdraga/develop
Add get users by affiliation.
2015-09-18 15:13:54 -04:00
Mike Taylor
55114bcffe Merge pull request #384 from elya5/patch-1
Fix UnboundlocalError in disco_browser.py example
2015-09-18 15:13:30 -04:00
Mike Taylor
4fa5dedc47 Merge pull request #386 from jdowner/develop-iot
iot: only add the 'done' field when all devices are done
2015-09-18 15:13:07 -04:00
Mike Taylor
5525ef2285 Merge pull request #395 from rerobins/refactor_forms
XEP_0004: Data Forms use register_stanza_plugin
2015-09-18 15:11:47 -04:00
Robert Robinson
a7ac969215 register_Stanza_plugin shouldn't be iterable
Should not use iterable for registering the stanza plugins.
2015-09-17 16:21:54 -06:00
Robert Robinson
329cb5a9f8 Add 0122 to plugin/__init__.py __all__ 2015-09-17 16:21:13 -06:00
Robert Robinson
d9b47b33f5 Update __init__.py
changed xep_0121 to xep_0122
2015-09-15 10:20:37 -06:00
Robert Robinson
3582ac9941 Merge branch 'add_xep_0122' of https://github.com/rerobins/SleekXMPP into add_xep_0122 2015-09-15 10:12:50 -06:00
Robert Robinson
2a127a57a7 Add test case Reported->Data Form Validation
Add a test case that will verify that reported fields can contain data form validation data.
2015-09-15 10:09:06 -06:00
Robert Robinson
7059400020 Merge branch 'refactor_forms' into add_xep_0122 2015-09-15 10:07:34 -06:00
Robert Robinson
0b14ef82d4 Add test case for reported and items
Previous stanza test cases didn't have test cases for reported and item field types in forms.   This fixes that issue.

Modified stanzabase to use an ordered dict so that can guarentee the that 'items' in a form are added after reported.  Also updated the set of interfaces that are stored in Form to be a ordered set.  Used the order set implementation from:  https://code.activestate.com/recipes/576694/

The OrderedSet implementation is licensed under the MIT license and is developed by the same developer of the ordereddict.
2015-09-15 10:05:53 -06:00
Robert Robinson
83953af53d Missing xep_122 dir in setup.py 2015-09-14 20:28:55 -06:00
Robert Robinson
110cf25c6d Add plugin support 2015-09-14 17:06:07 -06:00
Robert Robinson
f2bf6072ec Add plugin
(cherry picked from commit 2296d56)
2015-09-14 17:04:43 -06:00
Robert Robinson
5f9abe2e0e Working through test case issues.
(cherry picked from commit 6b58cef)
2015-09-14 17:04:16 -06:00
Robert Robinson
ea65b672e7 Initial cut at getting the stanzas to work.
(cherry picked from commit 8c7df49)
2015-09-14 17:04:08 -06:00
Robert Robinson
93c705fb31 Fix xep_0050 changes after form refactor. 2015-09-14 17:00:53 -06:00
Robert Robinson
0724f623bb Force forms and fields to use plugin resolution
Instead of using the interface/subinterface code that was currently being implemented for the plugin.
(cherry picked from commit 1467ec7)
2015-09-14 16:46:36 -06:00
mathieui
82e549c0e9 (Temporary) fix for python 3.5
This will work until the old ssl implementation is finally deprecated.
Hopefully, new features to painlessy implement starttls will be around
by then.
2015-09-14 23:14:53 +02:00
mathieui
1aa15792b4 Bump the requirements to aiodns 1.0
(and use install_requires instead of requires in the setup.py)
2015-09-14 23:14:06 +02:00
Robert Robinson
ffb2b6bc04 Update test_stream_xep_0050.py
Fix Unit Test for adhoc 50 stream.
2015-09-12 20:08:21 -06:00
Emmanuel Gil Peyrot
27f98bf22c xep_0231: Fix a traceback on result serialization. 2015-09-05 18:35:59 +01:00
mathieui
3978078710 vcard-temp: add some checks against wrong input 2015-09-04 01:59:40 +02:00
mathieui
00a0698720 Add timeout_callback to a bunch of plugins as a parameter 2015-09-04 01:05:56 +02:00
Robert Robinson
4a24f58be2 XEP0050: Add support for payload in completed response
When sending the command to complete the task, the adhoc plugin does not provide the ability to send a payload from the _handle_command_complete method.
2015-09-03 10:15:41 -06:00
Mike Taylor
da14ce16ec Merge pull request #394 from sangeeths/misc_updates
adding 'id' to self['xep_0332'].send_request()
2015-08-27 13:00:34 -04:00
Sangeeth Saravanaraj
18e5abb9dd adding 'id' to self['xep_0332'].send_request() 2015-08-27 13:24:01 +05:30
Richard Chan
1a75b76916 Only send time if Iq type is get. 2015-08-25 18:21:58 +08:00
Richard Chan
53b56899a0 Do not overwrite telephone numbers; otherwise all TEL/NUMBER received
from a server will be blank.
2015-08-25 18:11:54 +08:00
mathieui
804b23d390 Merge branch 'socks5' of http://git.linkmauve.fr/slixmpp 2015-08-23 17:14:53 +02:00
Emmanuel Gil Peyrot
04eaf52b1d xep_0065: Remove an unused variable. 2015-08-23 16:06:01 +01:00
Emmanuel Gil Peyrot
dc7fef1064 xep_0065: Remove the last useless threading locks. 2015-08-23 16:06:01 +01:00
Emmanuel Gil Peyrot
488c433555 Add SOCKS5 Bytestream examples. 2015-08-23 16:06:01 +01:00
Emmanuel Gil Peyrot
9c5dd024b1 Fix the xep_0065 plugin, by rewriting its socks5 implementation. 2015-08-23 16:06:01 +01:00
Florent Le Coz
6e61adf3db Fix the order in which <identity/> and <feature/> tags are sent on disco#info
The identities should all be at the start, and features at the end, so we
just prepend the identity on add_identity, and append features on
add_feature
2015-08-22 18:48:29 +02:00
Emmanuel Gil Peyrot
041bd63864 Add a function to convert a domain name to punycode. 2015-08-20 20:04:58 +01:00
Aleksandr Draga
a366482551 Add get users by affiliation. 2015-08-10 15:34:27 +03:00
Emmanuel Gil Peyrot
a721084f6e Fix the pubsub_client example. 2015-08-08 17:34:06 +02:00
Emmanuel Gil Peyrot
1b4187fa56 Add a format() method to XMPPError which returns a readable string. 2015-08-08 17:34:06 +02:00
Emmanuel Gil Peyrot
cf7a60705e Fix docstring of unsubscribe method in the PubSub plugin. 2015-08-08 17:34:06 +02:00
Emmanuel Gil Peyrot
349b05b9b7 Stop disco_browser and pubsub_client examples once they are finished. 2015-08-08 17:34:06 +02:00
Emmanuel Gil Peyrot
9fbacf377a Strip strings after pygments, so we don’t include an needless newline. 2015-08-08 17:34:06 +02:00
mathieui
2da9e35cbc Add missing files to the MANIFEST 2015-08-08 17:34:06 +02:00
Mike Taylor
abcec1e2d3 Merge pull request #388 from sangeeths/misc_updates
Retaining 'id' in the response and error stanzas
2015-08-01 14:04:22 -04:00
Sangeeth Saravanaraj
eeab646bfa Retaining 'id' in the response and error stanzas 2015-08-01 17:47:03 +05:30
Mike Taylor
2c69144189 Merge pull request #387 from mcella/378
Fixes #378: must acquire JID_CACHE_LOCK before adding to JID_CACHE
2015-07-31 11:21:01 -04:00
Michele Cella
f54ebec654 Fixes #378: must acquire JID_CACHE_LOCK before adding to JID_CACHE 2015-07-31 11:55:50 +02:00
Joshua Downer
2042e1a4d5 iot: only add the 'done' field when all devices are done 2015-07-20 17:34:09 -04:00
Robert Robinson
be14f0cc52 XEP_0050: Form not iterable in command
Cannot pass in a form into the initial command and have it show up in the payload of the session.  Line 344 makes this possible when following the standard workflow.
2015-07-15 20:52:06 -06:00
elya5
edd9199be8 Fix UnboundlocalError in disco_browser.py example
If self.get is in self.info_types and self.items_types, only self['xep_0030'].get_info is executed and not self['xep_0030'].get_items. So the condition in line 129 is successful but items is not assigned.
2015-07-09 17:15:36 +02:00
Mike Taylor
bb094cc649 Merge pull request #365 from jdowner/staging
Fixed imports
2015-07-05 15:46:04 -04:00
Mike Taylor
dbaa6ed952 Merge pull request #366 from jdowner/develop-iot-cleanup
Minor cleanup of IoT plugin
2015-07-05 15:45:47 -04:00
Mike Taylor
8c94d894ab Merge pull request #369 from stevenroose/patch-2
Change to roster migration e
2015-07-05 15:45:19 -04:00
Mike Taylor
ffc7eac4dc Merge pull request #370 from jdowner/develop-jid
Removed duplicate property
2015-07-05 15:44:58 -04:00
Mike Taylor
555fd6d926 Merge pull request #380 from anirudh-chhangani/XEP-0096-add-hash-param
add hash metadata for file transfer
2015-07-05 15:44:03 -04:00
Mike Taylor
c024ac8f0b Merge pull request #382 from sangeeths/initialize_certificate
Initialize certfile, keyfile and ca_certs in XMLStream. Added **kwargs to ClientXMPP, BaseXMPP and XMLStream.
2015-07-03 15:07:35 -04:00
Sangeeth Saravanaraj
f00177c0cf Added **kwargs to ClientXMPP, BaseXMPP and XMLStream so that certfile, keyfile and ca_certs can be initialized. 2015-07-03 10:47:06 +05:30
Anirudh
224d7ae133 add hash param to file metadata 2015-06-18 00:21:19 +05:30
Sangeeth Saravanaraj
9b25a7cf77 Fixed typo. 2015-06-05 12:25:41 +05:30
Joshua Downer
7a908ac07b Removed duplicate property 2015-05-28 09:35:50 -04:00
Steven Roose
92901637ec Change to roster migration example
I did have the chance to test the script yet, but it seems like that line should be outside the for loop.
2015-05-25 01:01:08 +02:00
Joshua Downer
3590b663ed xep-0323: removed deadcode 2015-05-14 06:27:59 -04:00
Joshua Downer
a33bde9cc3 xep-0323: spelling 2015-05-14 06:27:39 -04:00
Joshua Downer
ac50fdccfc xep-0323: unused import 2015-05-14 06:26:54 -04:00
Joshua Downer
a0c6bf15e9 Fixed imports
Removed unused modules/packages and added getpass, which was missing.
2015-05-13 17:24:06 -04:00
Mike Taylor
a8ac115310 Merge pull request #363 from sangeeths/xep_0332
XEP_332: Prefixed request and response with "http"
2015-05-01 12:50:06 -04:00
Sangeeth Saravanaraj
1345b7c1d0 Misc updates for send_error() 2015-05-01 15:34:53 +05:30
Sangeeth Saravanaraj
d60a652259 data need not be prefixed with http.. 2015-05-01 14:32:36 +05:30
Sangeeth Saravanaraj
61a7cecb31 Prefixed request, response and data with http. Avoided (plugin_attrib) name collision with other plugins. 2015-04-29 14:44:25 +05:30
Mike Taylor
192b7e0349 Merge pull request #345 from sangeeths/xep_0332
XEP-0332: HTTP over XMPP transport
2015-04-28 22:44:27 -04:00
Sangeeth Saravanaraj
80b60fc048 Merge remote-tracking branch 'origin/develop' into xep_0332 2015-04-28 16:53:40 +05:30
Mike Taylor
842157a6cc Merge pull request #187 from ekini/xep_0138
added xep-0138 support (compression)
2015-04-11 20:58:45 -04:00
Mike Taylor
a63cc01482 Merge pull request #316 from rakoo/develop
Extend AtomEntry capabilities
2015-04-11 20:53:44 -04:00
bear (Mike Taylor)
1bbb6f3ff9 Merge branch 'hildjj-develop' into develop 2015-04-11 20:43:56 -04:00
bear (Mike Taylor)
93894247a4 Merge branch 'develop' of https://github.com/hildjj/SleekXMPP into hildjj-develop 2015-04-11 20:42:33 -04:00
Mike Taylor
16bb5e2537 bump to version v1.4 2015-04-11 20:38:11 -04:00
Mike Taylor
d19a6e05b2 remove python v3.1 - v3.3 from tox.ini 2015-04-11 20:37:05 -04:00
Mike Taylor
86e85f9835 Merge pull request #313 from mayflower/develop
Proposing #310 again in fixed version
2015-04-11 20:12:19 -04:00
Mike Taylor
cc145d20b0 Merge pull request #297 from keith-gray-powereng/develop
Fixed a unicode error in xep_0065 on Python 3
2015-04-11 19:49:43 -04:00
Mike Taylor
881d9040c4 Merge pull request #329 from FlySnake/send_queue_overflow
In queues added option to remove first element on addind new if queue is full
2015-04-11 19:46:26 -04:00
Mike Taylor
1e77ea0944 Merge pull request #328 from FlySnake/develop
On initial connect use delay if connection failed
2015-04-11 19:20:39 -04:00
Mike Taylor
140f0885b2 Merge pull request #331 from mathieui/develop
Fix the element name for retrieving certs in XEP-0257
2015-04-11 19:15:26 -04:00
Mike Taylor
83f71a6610 Merge pull request #348 from gribouille-dev/tor_fixes
Makes XEP-0009 compatible with Python 2 & 3.
2015-04-11 18:32:42 -04:00
Mike Taylor
271343a32d Merge pull request #349 from mulog1990/ssl-version-fix
ssl-version not passed to wrap_socket, fixed
2015-04-11 18:26:05 -04:00
Mike Taylor
48857b0030 Merge pull request #354 from erigones/develop
Fixed bug #353 Python3 XEP-0084 error
2015-04-11 18:12:40 -04:00
Mike Taylor
1fe7f5f4e6 Create .travis.yml 2015-04-11 17:45:23 -04:00
Richard Kellner
81b7b2c190 Fixed bug #353 Python3 XEP-0084 error 2015-03-25 14:04:46 +01:00
mulog1990
460de7d301 ssl-version not passed to wrap_socket, fixed 2015-03-10 18:13:53 +08:00
Cédric Souchon
69022c6db7 Makes XEP-0009 compatible with Python 3 while maintaining compatibility with Python 2.6 and up. 2015-03-09 12:33:18 +01:00
Sangeeth Saravanaraj
9044807121 Added help for running example.. 2015-02-05 18:11:41 +05:30
Sangeeth Saravanaraj
24264d3a07 Updated Example.. 2015-02-05 18:10:10 +05:30
Sangeeth Saravanaraj
8bc70264ef misc updates.. 2015-02-05 17:35:04 +05:30
Sangeeth Saravanaraj
c16b862200 Raise http_request and http_response events. 2015-02-03 12:33:25 +05:30
Sangeeth Saravanaraj
a96f608469 Composing request and response. 2015-01-29 08:33:40 +05:30
Sangeeth Saravanaraj
e1f25604ec Added callbacks, registered stanzas, added features, etc. 2015-01-28 14:52:15 +05:30
Sangeeth Saravanaraj
0fe057b5c3 Boilerplate for Stanzas - request and response 2015-01-27 15:13:57 +05:30
Sangeeth Saravanaraj
be76dda21d Added xep_0332 to setup 2015-01-23 10:29:21 +05:30
Sangeeth Saravanaraj
ecd124dd06 Boilerplate for xep_0332 2015-01-22 16:40:03 +05:30
Sangeeth Saravanaraj
4a8951c4ee added xep_0332 to plugins 2015-01-22 16:39:27 +05:30
Sangeeth Saravanaraj
8afba7de85 renamed example for convenience. 2015-01-22 16:38:16 +05:30
Sangeeth Saravanaraj
1ce42d3a2f Boilerplate example. 2015-01-22 11:30:38 +05:30
Sangeeth Saravanaraj
2f4d811db4 Fixed a typo in docs/guide_xep_0030.rst 2015-01-22 11:13:03 +05:30
Sangeeth Saravanaraj
61127f521d Added PyCharm's .idea folder to .gitignore 2015-01-22 11:09:47 +05:30
mathieui
063e73c0d2 Fix the element name for retrieving certs in XEP-0257
And s/258/257/ in the XEP description
2014-12-11 18:32:50 +01:00
Oleg Antonyan
d261318e1a In queues added option to remove first element on addind new if queue is
full
2014-11-27 07:11:06 +02:00
Oleg Antonyan
d33cc00fe9 On initial connect use delay if connection failed 2014-11-23 16:46:01 +02:00
Lance Stout
27582f6fd2 Merge pull request #326 from s-m-b/patch-1
Typo fix of parameter name 'data' it is now 'iq'
2014-11-10 09:07:32 -08:00
s-m-b
e328ff4833 Typo fix of parameter name 'data' it is now 'iq'
Code was broken during refactoring
2014-11-09 04:36:38 +03:00
Lance Stout
403462fdb8 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2014-09-09 08:50:24 -07:00
Lance Stout
f22d8e67b4 Preserve ID for error responses
Fixes #319
2014-09-09 08:49:37 -07:00
Matthieu Rakotojaona
35f33f1614 Extend AtomEntry capabilities 2014-08-30 17:23:27 +02:00
Lance Stout
c9f8ddff65 Merge pull request #315 from louiz/develop
Fix saslprep on the username
2014-08-24 16:19:15 -07:00
Florent Le Coz
f5ae98aaf1 Fix saslprep on the username
Two issues fixed here:

- ints are not comparable with bytes, so char was never == to b',', which
  renders the whole function pointless
- The bytes were converted back to “characters” by using chr(), which
  doesn’t make sense if the username contains characters that fit on more
  than one bytes. This would trigger an “invalid username” error from the
  server when using a non-ascii JID.
2014-08-25 01:08:13 +02:00
Robin Gloster
073e85381a fix args, kwargs which were broken with #310. this is essentially the same but working 2014-08-23 14:25:35 +02:00
Robin Gloster
afc939708f cleanup semicolons, whitespace and mutable default arguments 2014-08-23 12:47:29 +02:00
Lance Stout
aabec8b993 Fix some more Unicode in **kwargs issues in Py2.6 2014-08-21 10:05:42 -07:00
Lance Stout
e5e2fbb16b Merge pull request #311 from Mayflower/develop
Revert "cleanup semicolons, whitespace and mutable default arguments"
2014-08-18 13:34:15 -07:00
Robin Gloster
3dd379cdf1 Revert "cleanup semicolons, whitespace and mutable default arguments"
This reverts commit 7265682a4d.
2014-08-18 15:15:14 +02:00
Lance Stout
a20582aba4 Merge pull request #309 from Mayflower/whitespace_keepalive
only schedule whitespace keepalive if enabled
2014-08-17 17:21:25 -07:00
Lance Stout
09cdbf1b76 Merge pull request #308 from Mayflower/develop
Serialize JID to allow json serializing
2014-08-17 17:20:45 -07:00
Lance Stout
ca306e7cec Merge pull request #310 from Mayflower/cleanup
Cleanup
2014-08-17 17:20:26 -07:00
Robin Gloster
1bf34f7fe6 fix mutable default arguments 💥 2014-08-18 00:55:10 +02:00
Robin Gloster
4144d60017 cleanup semicolons, whitespace and mutable default arguments 2014-08-18 00:55:10 +02:00
Robin Gloster
7265682a4d cleanup semicolons, whitespace and mutable default arguments 2014-08-18 00:52:24 +02:00
Robin Gloster
08c62a6bf1 fix mutable default arguments 💥 2014-08-18 00:18:10 +02:00
Robin Gloster
d61f1cd035 only schedule whitespace keepalive if enabled 2014-08-17 23:38:07 +02:00
Robin Gloster
1063feb33b only schedule whitespace keepalive if enabled 2014-08-17 23:37:19 +02:00
Robin Gloster
79f3c1ac8f serialize JID to allow json serializing 2014-08-17 23:13:56 +02:00
Lance Stout
a5c03b763a Merge pull request #305 from trinque/develop
Added wait param to XEP_0009 RemoteSession.close
2014-08-11 14:08:56 -07:00
Michael Trinque
3670d82f1c Added wait param to XEP_0009 RemoteSession.close
This parameter is False by default to preserve existing behavior.
2014-08-10 16:02:10 -07:00
Keith Gray
e94a73553d New version of the socks library socksipy from https://code.googlle.com/p/socksipy-branch/ 2014-06-15 19:01:19 -05:00
Keith Gray
577fd71472 Fixed a unicode error in xep_0065 on Python 3 2014-06-15 18:40:58 -05:00
Joe Hildebrand
ef1c4368d0 Merge branch 'master' of git://github.com/fritzy/SleekXMPP into develop
# By Joe Hildebrand (2) and Lance Stout (1)
# Via Lance Stout
* 'master' of git://github.com/fritzy/SleekXMPP:
  Relax timing issues in Iq timeout callback test.
  update JID_CACHE logic again.
  Allow IQ timeouts to be asynchronous, by passing a timeout_callback parameter to send().  An example modification of disco is included.  If this approach is approved, I'll go through and update the other plugins.

Conflicts:
	tests/test_stream_handlers.py
2012-10-31 14:44:51 -06:00
Joe Hildebrand
48def71d0c Merge branch 'master' of git://github.com/fritzy/SleekXMPP into develop
# By Lance Stout
# Via Lance Stout
* 'master' of git://github.com/fritzy/SleekXMPP:
  Turns out not all data is UTF-8, so don't try to decode it.
2012-10-31 12:49:33 -06:00
Joe Hildebrand
c8c20fff71 update JID_CACHE logic again. 2012-10-29 14:15:07 -06:00
Joe Hildebrand
75a18b5ffe Allow IQ timeouts to be asynchronous, by passing a timeout_callback parameter to send(). An example modification of disco is included. If this approach is approved, I'll go through and update the other plugins. 2012-10-29 10:03:32 -06:00
ekini
ea3d39b50e added xep-0138 support (compression) 2012-07-23 15:39:07 +07:00
369 changed files with 7542 additions and 3017 deletions

13
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,13 @@
################ Please use Gitlab instead of Github ###################################
Hello, thank you for contributing to slixmpp!
Youre 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.

1
.gitignore vendored
View File

@@ -12,3 +12,4 @@ slixmpp.egg-info/
*~ *~
.baboon/ .baboon/
.DS_STORE .DS_STORE
.idea/

21
.gitlab-ci.yml Normal file
View 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

7
.travis.yml Normal file
View File

@@ -0,0 +1,7 @@
language: python
python:
- "3.7"
- "3.8-dev"
install:
- "pip install ."
script: testall.py

14
CONTRIBUTING.rst Normal file
View 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 Slixmpps 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.

View File

@@ -1,6 +1,7 @@
Pre-requisites: Pre-requisites:
- Python 3.4 - Python 3.7+
- Cython 0.22 and libidn, optionally (making JID faster by compiling the stringprep module) - Cython 0.22 and libidn, optionally (making JID faster by compiling the stringprep module)
- GnuPG, for testing
Install: Install:
> python3 setup.py install > python3 setup.py install

25
LICENSE
View File

@@ -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 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 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 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.

View File

@@ -1,6 +1,7 @@
include README.rst include README.rst
include LICENSE include LICENSE
include testall.py include run_tests.py
include slixmpp/stringprep.pyx
recursive-include docs Makefile *.bat *.py *.rst *.css *.ttf *.png recursive-include docs Makefile *.bat *.py *.rst *.css *.ttf *.png
recursive-include examples *.py recursive-include examples *.py
recursive-include tests *.py recursive-include tests *.py

View File

@@ -1,13 +1,21 @@
Slixmpp 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. SleekXMPP.
Slixmpp's goals is to only rewrite the core of the library (the low level Slixmpp's goals is to only rewrite the core of the library (the low level
socket handling, the timers, the events dispatching) in order to remove all socket handling, the timers, the events dispatching) in order to remove all
threads. threads.
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::
python3 setup.py build_ext --inplace
Documentation and Testing Documentation and Testing
------------------------- -------------------------
@@ -28,7 +36,7 @@ The Slixmpp Boilerplate
------------------------- -------------------------
Projects using Slixmpp tend to follow a basic pattern for setting up client/component 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 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:: Slixmpp projects::
import logging import logging
@@ -94,8 +102,18 @@ Slixmpp projects::
Slixmpp Credits Slixmpp Credits
--------------- ---------------
**Maintainer of the slixmpp fork:** Florent Le Coz **Maintainers:**
`louiz@louiz.org <xmpp:louiz@louiz.org?message>`_, - 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) Credits (SleekXMPP)
------------------- -------------------

View File

@@ -408,24 +408,3 @@ div.viewcode-block:target {
margin: -1px -12px; margin: -1px -12px;
padding: 0 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;
}

View File

@@ -65,6 +65,5 @@
<div class="bottomnav"> <div class="bottomnav">
{{ nav() }} {{ nav() }}
</div> </div>
<a id="from_andyet" href="http://andyet.net"><h2>From &amp;yet</h2></a>
{% endblock %} {% endblock %}

View File

@@ -13,7 +13,7 @@ hides namespaces when able and does not introduce excessive namespace
prefixes:: prefixes::
>>> from slixmpp.xmlstream.tostring import tostring >>> 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>') >>> xml = ET.fromstring('<foo xmlns="bar"><baz /></foo>')
>>> ET.tostring(xml) >>> ET.tostring(xml)
'<ns0:foo xmlns:ns0="bar"><ns0:baz /></foo>' '<ns0:foo xmlns:ns0="bar"><ns0:baz /></foo>'

View File

@@ -12,12 +12,17 @@
# serve to show the default. # serve to show the default.
import sys, os import sys, os
import datetime
# If extensions (or modules to document with autodoc) are in another directory, # 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 # 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. # documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..')) 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 ----------------------------------------------------- # -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here. # If your documentation needs a minimal Sphinx version, state it here.
@@ -41,16 +46,18 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = u'Slixmpp' 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 # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
# built documents. # built documents.
# #
# auto imported from code!
# The short X.Y version. # The short X.Y version.
version = '1.0' # version = '1.4'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '1.0' # release = '1.4.0'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View File

@@ -163,7 +163,7 @@ behaviour:
namespace = 'jabber:iq:register' namespace = 'jabber:iq:register'
name = 'query' name = 'query'
plugin_attrib = 'register' plugin_attrib = 'register'
interfaces = set(('username', 'password', 'registered', 'remove')) interfaces = {'username', 'password', 'registered', 'remove'}
sub_interfaces = interfaces sub_interfaces = interfaces
def getRegistered(self): def getRegistered(self):
@@ -535,10 +535,10 @@ with some additional registration fields implemented.
namespace = 'jabber:iq:register' namespace = 'jabber:iq:register'
name = 'query' name = 'query'
plugin_attrib = 'register' plugin_attrib = 'register'
interfaces = set(('username', 'password', 'email', 'nick', 'name', interfaces = {'username', 'password', 'email', 'nick', 'name',
'first', 'last', 'address', 'city', 'state', 'zip', 'first', 'last', 'address', 'city', 'state', 'zip',
'phone', 'url', 'date', 'misc', 'text', 'key', 'phone', 'url', 'date', 'misc', 'text', 'key',
'registered', 'remove', 'instructions')) 'registered', 'remove', 'instructions'}
sub_interfaces = interfaces sub_interfaces = interfaces
def getRegistered(self): def getRegistered(self):

View File

@@ -3,8 +3,9 @@
Differences from SleekXMPP Differences from SleekXMPP
========================== ==========================
**Python 3.4+ only** **Python 3.7+ only**
slixmpp will only work on python 3.4 and above. slixmpp will work on python 3.7 and above. It may work with previous
versions but we provide no guarantees.
**Stanza copies** **Stanza copies**
The same stanza object is given through all the handlers; a handler that The same stanza object is given through all the handlers; a handler that

View File

@@ -259,8 +259,8 @@ Event Index
Signal that a connection to the XMPP server has been lost and the current Signal that a connection to the XMPP server has been lost and the current
stream session has ended. Currently equivalent to :term:`disconnected`, but stream session has ended. Currently equivalent to :term:`disconnected`, but
future implementation of `XEP-0198: Stream Management <http://xmpp.org/extensions/xep-0198.html>`_ implementations of `XEP-0198: Stream Management <http://xmpp.org/extensions/xep-0198.html>`_
will distinguish the two events. distinguish between the two events.
Plugins that maintain session-based state should clear themselves when Plugins that maintain session-based state should clear themselves when
this event is fired. this event is fired.

View File

@@ -11,7 +11,7 @@ Create and Run a Server Component
<xmpp:slixmpp@muc.poez.io?join>`_. <xmpp:slixmpp@muc.poez.io?join>`_.
If you have not yet installed Slixmpp, do so now by either checking out a version 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 Many XMPP applications eventually graduate to requiring to run as a server
component in order to meet scalability requirements. To demonstrate how to component in order to meet scalability requirements. To demonstrate how to

View File

@@ -11,7 +11,7 @@ Slixmpp Quickstart - Echo Bot
<xmpp:slixmpp@muc.poez.io?join>`_. <xmpp:slixmpp@muc.poez.io?join>`_.
If you have not yet installed Slixmpp, do so now by either checking out a version 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 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 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): class EchoBot(slixmpp.ClientXMPP):
def __init__(self, jid, password): def __init__(self, jid, password):
super(EchoBot, self).__init__(jid, password) super().__init__(jid, password)
Handling Session Start 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 .. code-block:: python
def __init__(self, jid, password): 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('session_start', self.start)
@@ -153,7 +153,7 @@ whenever a messsage is received.
.. code-block:: python .. code-block:: python
def __init__(self, jid, password): 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('session_start', self.start)
self.add_event_handler('message', self.message) 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 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:: .. compound::

View File

@@ -1,7 +1,7 @@
.. _mucbot: .. _mucbot:
========================= =========================
Mulit-User Chat (MUC) Bot Multi-User Chat (MUC) Bot
========================= =========================
.. note:: .. note::
@@ -11,7 +11,7 @@ Mulit-User Chat (MUC) Bot
<xmpp:slixmpp@muc.poez.io?join>`_. <xmpp:slixmpp@muc.poez.io?join>`_.
If you have not yet installed Slixmpp, do so now by either checking out a version 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 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 echobot example (:ref:`echobot`), we can use one of the bundled plugins
@@ -63,13 +63,13 @@ has been established:
def start(self, event): def start(self, event):
self.get_roster() self.get_roster()
self.send_presence() self.send_presence()
self.plugin['xep_0045'].joinMUC(self.room, self.plugin['xep_0045'].join_muc(self.room,
self.nick, self.nick,
wait=True) wait=True)
Note that as in :ref:`echobot`, we need to include send an initial presence and request 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 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:: .. note::

View File

@@ -24,7 +24,7 @@ for the JID that will receive our message, and the string content of the message
class SendMsgBot(slixmpp.ClientXMPP): class SendMsgBot(slixmpp.ClientXMPP):
def __init__(self, jid, password, recipient, msg): def __init__(self, jid, password, recipient, msg):
super(SendMsgBot, self).__init__(jid, password) super().__init__(jid, password)
self.recipient = recipient self.recipient = recipient
self.msg = msg 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) self.send_message(mto=self.recipient, mbody=self.msg)
Finally, we need to disconnect the client using :meth:`disconnect <slixmpp.xmlstream.XMLStream.disconnect>`. 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 Now, sent stanzas are placed in a queue to pass them to the send thread.
:meth:`disconnect <slixmpp.xmlstream.XMLStream.disconnect>` without any parameters, then it is possible :meth:`disconnect <slixmpp.xmlstream.XMLStream.disconnect>` by default will wait for an
for the client to disconnect before the send queue is processed and the message is actually acknowledgement from the server for at least `2.0` seconds. This time is configurable with
sent on the wire. To ensure that our message is processed, we use the `wait` parameter. If `0.0` is passed for `wait`, :meth:`disconnect
:meth:`disconnect(wait=True) <slixmpp.xmlstream.XMLStream.disconnect>`. <slixmpp.xmlstream.XMLStream.disconnect>` will not close the connection gracefully.
.. code-block:: python .. 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.send_message(mto=self.recipient, mbody=self.msg)
self.disconnect(wait=True) self.disconnect()
.. warning:: .. warning::
If you happen to be adding stanzas to the send queue faster than the send thread 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. will block and not disconnect.
Final Product Final Product

View File

@@ -61,7 +61,7 @@ operation using these stanzas without doing any complex operations such as
checking an ACL, etc. checking an ACL, etc.
You may find it necessary at some point to revert a particular node or JID to 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. You may also elect to only convert a given set of actions instead.
Creating a Node Handler Creating a Node Handler
@@ -161,8 +161,8 @@ item itself, and the JID and node that will own the item.
In this case, the owning JID and node are provided with the In this case, the owning JID and node are provided with the
parameters ``ijid`` and ``node``. parameters ``ijid`` and ``node``.
Peforming Disco Queries Performing Disco Queries
----------------------- ------------------------
The methods ``get_info()`` and ``get_items()`` are used to query remote JIDs The methods ``get_info()`` and ``get_items()`` are used to query remote JIDs
and their nodes for disco information. Since these methods are wrappers for and their nodes for disco information. Since these methods are wrappers for
sending Iq stanzas, they also accept all of the parameters of the ``Iq.send()`` sending Iq stanzas, they also accept all of the parameters of the ``Iq.send()``
@@ -172,11 +172,10 @@ the `XEP-0059 <http://xmpp.org/extensions/xep-0059.html>`_ plug-in.
.. code-block:: python .. code-block:: python
info = self['xep_0030'].get_info(jid='foo@example.com', info = yield from self['xep_0030'].get_info(jid='foo@example.com',
node='bar', node='bar',
ifrom='baz@mycomponent.example.com', ifrom='baz@mycomponent.example.com',
block=True, timeout=30)
timeout=30)
items = self['xep_0030'].get_info(jid='foo@example.com', items = self['xep_0030'].get_info(jid='foo@example.com',
node='bar', node='bar',

View File

@@ -4,9 +4,9 @@ Slixmpp
.. sidebar:: Get the Code .. sidebar:: Get the Code
The latest source code for Slixmpp may be found on the `Git repo 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. 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>`_ `slixmpp@muc.poez.io <xmpp:slixmpp@muc.poez.io?join>`_
**Reporting bugs** **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:: .. note::
slixmpp is a friendly fork of `SleekXMPP <https://github.com/fritzy/SleekXMPP>`_ 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 which goal is to use asyncio instead of threads to handle networking. See
:ref:`differences`. :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: Slixmpp's design goals and philosphy are:

View File

@@ -160,9 +160,9 @@ if __name__ == '__main__':
myDevice = TheDevice(args.nodeid); myDevice = TheDevice(args.nodeid);
# myDevice._add_field(name="Relay", typename="numeric", unit="Bool"); # myDevice._add_field(name="Relay", typename="numeric", unit="Bool");
myDevice._add_field(name="Temperature", typename="numeric", unit="C"); myDevice._add_field(name="Temperature", typename="numeric", unit="C")
myDevice._set_momentary_timestamp("2013-03-07T16:24:30") myDevice._set_momentary_timestamp("2013-03-07T16:24:30")
myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"}); myDevice._add_field_momentary_data("Temperature", "23.4", flags={"automaticReadout": "true"})
xmpp['xep_0323'].register_node(nodeId=args.nodeid, device=myDevice, commTimeout=10); xmpp['xep_0323'].register_node(nodeId=args.nodeid, device=myDevice, commTimeout=10);
xmpp.beClientOrServer(server=True) xmpp.beClientOrServer(server=True)

View File

@@ -68,7 +68,7 @@ class CommandBot(slixmpp.ClientXMPP):
session. Additional, custom data may be saved session. Additional, custom data may be saved
here to persist across handler callbacks. 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['instructions'] = 'Send a custom greeting to a JID'
form.addField(var='greeting', form.addField(var='greeting',
ftype='text-single', ftype='text-single',

View File

@@ -94,7 +94,7 @@ class CommandUserBot(slixmpp.ClientXMPP):
# label="Your greeting" /> # label="Your greeting" />
# </x> # </x>
form = self['xep_0004'].makeForm(ftype='submit') form = self['xep_0004'].make_form(ftype='submit')
form.addField(var='greeting', form.addField(var='greeting',
value=session['greeting']) value=session['greeting'])

99
examples/confirm_answer.py Executable file
View 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
View 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)

View File

@@ -50,7 +50,7 @@ class ActionBot(slixmpp.ClientXMPP):
register_stanza_plugin(Iq, Action) register_stanza_plugin(Iq, Action)
def start(self, event): async def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -73,7 +73,7 @@ class ActionBot(slixmpp.ClientXMPP):
""" """
self.event('custom_action', iq) self.event('custom_action', iq)
def _handle_action_event(self, iq): async def _handle_action_event(self, iq):
""" """
Respond to the custom action event. Respond to the custom action event.
""" """
@@ -82,17 +82,20 @@ class ActionBot(slixmpp.ClientXMPP):
if method == 'is_prime' and param == '2': if method == 'is_prime' and param == '2':
print("got message: %s" % iq) print("got message: %s" % iq)
iq.reply() rep = iq.reply()
iq['action']['status'] = 'done' rep['action']['status'] = 'done'
iq.send() await rep.send()
elif method == 'bye': elif method == 'bye':
print("got message: %s" % iq) print("got message: %s" % iq)
rep = iq.reply()
rep['action']['status'] = 'done'
await rep.send()
self.disconnect() self.disconnect()
else: else:
print("got message: %s" % iq) print("got message: %s" % iq)
iq.reply() rep = iq.reply()
iq['action']['status'] = 'error' rep['action']['status'] = 'error'
iq.send() await rep.send()
if __name__ == '__main__': if __name__ == '__main__':
# Setup the command line arguments. # Setup the command line arguments.

View File

@@ -43,7 +43,7 @@ class ActionUserBot(slixmpp.ClientXMPP):
register_stanza_plugin(Iq, Action) register_stanza_plugin(Iq, Action)
def start(self, event): async def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -57,11 +57,11 @@ class ActionUserBot(slixmpp.ClientXMPP):
data. data.
""" """
self.send_presence() 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. """Create and send two custom actions.
If the first action was successful, then send If the first action was successful, then send
@@ -74,14 +74,14 @@ class ActionUserBot(slixmpp.ClientXMPP):
iq['action']['param'] = '2' iq['action']['param'] = '2'
try: try:
resp = iq.send() resp = await iq.send()
if resp['action']['status'] == 'done': if resp['action']['status'] == 'done':
#sending bye #sending bye
iq2 = self.Iq() iq2 = self.Iq()
iq2['to'] = self.action_provider iq2['to'] = self.action_provider
iq2['type'] = 'set' iq2['type'] = 'set'
iq2['action']['method'] = 'bye' iq2['action']['method'] = 'bye'
iq2.send(block=False) await iq2.send()
self.disconnect() self.disconnect()
except XMPPError: except XMPPError:

View File

@@ -41,7 +41,7 @@ class Action(ElementBase):
#: del action['status'] #: del action['status']
#: #:
#: to set, get, or remove its values. #: 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 #: By default, values in the `interfaces` set are mapped to
#: attribute values. This can be changed such that an interface #: attribute values. This can be changed such that an interface

View File

@@ -15,7 +15,6 @@ from argparse import ArgumentParser
import slixmpp import slixmpp
from slixmpp.exceptions import IqError, IqTimeout from slixmpp.exceptions import IqError, IqTimeout
from slixmpp.xmlstream.asyncio import asyncio
class Disco(slixmpp.ClientXMPP): class Disco(slixmpp.ClientXMPP):
@@ -54,8 +53,7 @@ class Disco(slixmpp.ClientXMPP):
# our roster. # our roster.
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start)
@asyncio.coroutine async def start(self, event):
def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -76,18 +74,14 @@ class Disco(slixmpp.ClientXMPP):
try: try:
if self.get in self.info_types: if self.get in self.info_types:
# By using block=True, the result stanza will be
# returned. Execution will block until the reply is
# received. Non-blocking options would be to listen
# for the disco_info event, or passing a handler
# function using the callback parameter. # 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) node=self.target_node)
if self.get in self.items_types: if self.get in self.items_types:
# The same applies from above. Listen for the # The same applies from above. Listen for the
# disco_items event or pass a callback function # disco_items event or pass a callback function
# if you need to process a non-blocking request. # 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) node=self.target_node)
if self.get not in self.info_types and self.get not in self.items_types: if self.get not in self.info_types and self.get not in self.items_types:
logging.error("Invalid disco request type.") logging.error("Invalid disco request type.")
@@ -162,4 +156,4 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() xmpp.connect()
xmpp.process() xmpp.process(forever=False)

View File

@@ -47,8 +47,7 @@ class AvatarDownloader(slixmpp.ClientXMPP):
self.roster_received.set() self.roster_received.set()
self.presences_received.clear() self.presences_received.clear()
@asyncio.coroutine async def start(self, event):
def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -65,16 +64,15 @@ class AvatarDownloader(slixmpp.ClientXMPP):
self.get_roster(callback=self.roster_received_cb) self.get_roster(callback=self.roster_received_cb)
print('Waiting for presence updates...\n') print('Waiting for presence updates...\n')
yield from self.roster_received.wait() await self.roster_received.wait()
print('Roster received') print('Roster received')
yield from self.presences_received.wait() await self.presences_received.wait()
self.disconnect() self.disconnect()
@asyncio.coroutine async def on_vcard_avatar(self, pres):
def on_vcard_avatar(self, pres):
print("Received vCard avatar update from %s" % pres['from'].bare) print("Received vCard avatar update from %s" % pres['from'].bare)
try: 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) timeout=5)
except XMPPError: except XMPPError:
print("Error retrieving avatar for %s" % pres['from']) print("Error retrieving avatar for %s" % pres['from'])
@@ -89,14 +87,13 @@ class AvatarDownloader(slixmpp.ClientXMPP):
with open(filename, 'wb+') as img: with open(filename, 'wb+') as img:
img.write(avatar['BINVAL']) img.write(avatar['BINVAL'])
@asyncio.coroutine async def on_avatar(self, msg):
def on_avatar(self, msg):
print("Received avatar update from %s" % msg['from']) print("Received avatar update from %s" % msg['from'])
metadata = msg['pubsub_event']['items']['item']['avatar_metadata'] metadata = msg['pubsub_event']['items']['item']['avatar_metadata']
for info in metadata['items']: for info in metadata['items']:
if not info['url']: if not info['url']:
try: 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) timeout=5)
except XMPPError: except XMPPError:
print("Error retrieving avatar for %s" % msg['from']) print("Error retrieving avatar for %s" % msg['from'])

View File

@@ -55,8 +55,8 @@ class GTalkBot(slixmpp.ClientXMPP):
cert.verify('talk.google.com', der_cert) cert.verify('talk.google.com', der_cert)
logging.debug("CERT: Found GTalk certificate") logging.debug("CERT: Found GTalk certificate")
except cert.CertificateError as err: except cert.CertificateError as err:
log.error(err.message) logging.error(err.message)
self.disconnect(send_close=False) self.disconnect()
def start(self, event): def start(self, event):
""" """

View File

@@ -0,0 +1,97 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Slixmpp: The Slick XMPP Library
Implementation of HTTP over XMPP transport
http://xmpp.org/extensions/xep-0332.html
Copyright (C) 2015 Riptide IO, sangeeth@riptideio.com
This file is part of slixmpp.
See the file LICENSE for copying permission.
"""
from slixmpp import ClientXMPP
from argparse import ArgumentParser
import logging
import getpass
class HTTPOverXMPPClient(ClientXMPP):
def __init__(self, jid, password):
ClientXMPP.__init__(self, jid, password)
self.register_plugin('xep_0332') # HTTP over XMPP Transport
self.add_event_handler(
'session_start', self.session_start
)
self.add_event_handler('http_request', self.http_request_received)
self.add_event_handler('http_response', self.http_response_received)
def http_request_received(self, iq):
pass
def http_response_received(self, iq):
print('HTTP Response Received : %s' % iq)
print('From : %s' % iq['from'])
print('To : %s' % iq['to'])
print('Type : %s' % iq['type'])
print('Headers : %s' % iq['resp']['headers'])
print('Code : %s' % iq['resp']['code'])
print('Message : %s' % iq['resp']['message'])
print('Data : %s' % iq['resp']['data'])
def session_start(self, event):
# TODO: Fill in the blanks
self['xep_0332'].send_request(
to='?', method='?', resource='?', headers={}
)
self.disconnect()
if __name__ == '__main__':
#
# NOTE: To run this example, fill up the blanks in session_start() and
# use the following command.
#
# ./http_over_xmpp.py -J <jid> -P <pwd> -i <ip> -p <port> [-v]
#
parser = ArgumentParser()
# Output verbosity options.
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_argument('-J', '--jid', dest='jid', help='JID')
parser.add_argument('-P', '--password', dest='password', help='Password')
# XMPP server ip and port options.
parser.add_argument(
'-i', '--ipaddr', dest='ipaddr',
help='IP Address of the XMPP server', default=None
)
parser.add_argument(
'-p', '--port', dest='port',
help='Port of the XMPP server', default=None
)
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input('Username: ')
if args.password is None:
args.password = getpass.getpass('Password: ')
xmpp = HTTPOverXMPPClient(args.jid, args.password)
xmpp.connect()
xmpp.process()

96
examples/http_upload.py Executable file
View 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 servers)")
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)

View File

@@ -9,7 +9,6 @@
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import asyncio
import logging import logging
from getpass import getpass from getpass import getpass
from argparse import ArgumentParser from argparse import ArgumentParser
@@ -39,8 +38,7 @@ class IBBSender(slixmpp.ClientXMPP):
# our roster. # our roster.
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start)
@asyncio.coroutine async def start(self, event):
def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -58,13 +56,13 @@ class IBBSender(slixmpp.ClientXMPP):
try: try:
# Open the IBB stream in which to write to. # 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. # 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. # And finally close the stream.
yield from stream.close(timeout=10) await stream.close(timeout=10)
except (IqError, IqTimeout): except (IqError, IqTimeout):
print('File transfer errored') print('File transfer errored')
else: else:

97
examples/mam.py Executable file
View 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
View 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()

View File

@@ -100,7 +100,7 @@ def on_session2(event):
new_xmpp.update_roster(jid, new_xmpp.update_roster(jid,
name = item['name'], name = item['name'],
groups = item['groups']) groups = item['groups'])
new_xmpp.disconnect() new_xmpp.disconnect()
new_xmpp.add_event_handler('session_start', on_session2) new_xmpp.add_event_handler('session_start', on_session2)
new_xmpp.connect() new_xmpp.connect()

View File

@@ -67,11 +67,11 @@ class MUCBot(slixmpp.ClientXMPP):
""" """
self.get_roster() self.get_roster()
self.send_presence() self.send_presence()
self.plugin['xep_0045'].joinMUC(self.room, self.plugin['xep_0045'].join_muc(self.room,
self.nick, self.nick,
# If a room password is needed, use: # If a room password is needed, use:
# password=the_room_password, # password=the_room_password,
wait=True) wait=True)
def muc_message(self, msg): def muc_message(self, msg):
""" """

View File

@@ -13,7 +13,6 @@ import logging
from getpass import getpass from getpass import getpass
from argparse import ArgumentParser from argparse import ArgumentParser
from slixmpp.exceptions import IqError, IqTimeout from slixmpp.exceptions import IqError, IqTimeout
from slixmpp import asyncio
import slixmpp import slixmpp
@@ -38,8 +37,7 @@ class PingTest(slixmpp.ClientXMPP):
# our roster. # our roster.
self.add_event_handler("session_start", self.start) self.add_event_handler("session_start", self.start)
@asyncio.coroutine async def start(self, event):
def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -56,7 +54,7 @@ class PingTest(slixmpp.ClientXMPP):
self.get_roster() self.get_roster()
try: try:
rtt = yield from self['xep_0199'].ping(self.pingjid, rtt = await self['xep_0199'].ping(self.pingjid,
timeout=10) timeout=10)
logging.info("Success! RTT: %s", rtt) logging.info("Success! RTT: %s", rtt)
except IqError as e: except IqError as e:

View File

@@ -6,26 +6,21 @@ from getpass import getpass
from argparse import ArgumentParser from argparse import ArgumentParser
import slixmpp import slixmpp
from slixmpp.exceptions import XMPPError
from slixmpp.xmlstream import ET, tostring from slixmpp.xmlstream import ET, tostring
from slixmpp.xmlstream.asyncio import asyncio
def make_callback():
future = asyncio.Future()
def callback(result):
future.set_result(result)
return future, callback
class PubsubClient(slixmpp.ClientXMPP): class PubsubClient(slixmpp.ClientXMPP):
def __init__(self, jid, password, server, def __init__(self, jid, password, server,
node=None, action='list', data=''): node=None, action='nodes', data=''):
super(PubsubClient, self).__init__(jid, password) super().__init__(jid, password)
self.register_plugin('xep_0030') self.register_plugin('xep_0030')
self.register_plugin('xep_0059') self.register_plugin('xep_0059')
self.register_plugin('xep_0060') self.register_plugin('xep_0060')
self.actions = ['nodes', 'create', 'delete', self.actions = ['nodes', 'create', 'delete', 'get_configure',
'publish', 'get', 'retract', 'publish', 'get', 'retract',
'purge', 'subscribe', 'unsubscribe'] 'purge', 'subscribe', 'unsubscribe']
@@ -36,87 +31,89 @@ class PubsubClient(slixmpp.ClientXMPP):
self.add_event_handler('session_start', self.start) self.add_event_handler('session_start', self.start)
def start(self, event): async def start(self, event):
self.get_roster() self.get_roster()
self.send_presence() self.send_presence()
try: try:
getattr(self, self.action)() await getattr(self, self.action)()
except: except:
logging.error('Could not execute: %s' % self.action) logging.exception('Could not execute %s:', self.action)
self.disconnect() self.disconnect()
def nodes(self): async def nodes(self):
future, callback = make_callback()
try: try:
self['xep_0060'].get_nodes(self.pubsub_server, self.node, callback=callback) result = await self['xep_0060'].get_nodes(self.pubsub_server, self.node)
result = yield from future
for item in result['disco_items']['items']: for item in result['disco_items']['items']:
print(' - %s' % str(item)) logging.info(' - %s', str(item))
except: except XMPPError as error:
logging.error('Could not retrieve node list.') logging.error('Could not retrieve node list: %s', error.format())
def create(self): async def create(self):
try: try:
self['xep_0060'].create_node(self.pubsub_server, self.node) await self['xep_0060'].create_node(self.pubsub_server, self.node)
except: logging.info('Created node %s', self.node)
logging.error('Could not create 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: try:
self['xep_0060'].delete_node(self.pubsub_server, self.node) await self['xep_0060'].delete_node(self.pubsub_server, self.node)
print('Deleted node: %s' % self.node) logging.info('Deleted node %s', self.node)
except: except XMPPError as error:
logging.error('Could not delete node: %s' % self.node) 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) payload = ET.fromstring("<test xmlns='test'>%s</test>" % self.data)
future, callback = make_callback()
try: try:
self['xep_0060'].publish(self.pubsub_server, self.node, payload=payload, callback=callback) result = await self['xep_0060'].publish(self.pubsub_server, self.node, payload=payload)
result = yield from future logging.info('Published at item id: %s', result['pubsub']['publish']['item']['id'])
id = result['pubsub']['publish']['item']['id'] except XMPPError as error:
print('Published at item id: %s' % id) logging.error('Could not publish to %s: %s', self.node, error.format())
except:
logging.error('Could not publish to: %s' % self.node)
def get(self): async def get(self):
future, callback = make_callback()
try: try:
self['xep_0060'].get_item(self.pubsub_server, self.node, self.data, callback=callback) result = await self['xep_0060'].get_item(self.pubsub_server, self.node, self.data)
result = yield from future
for item in result['pubsub']['items']['substanzas']: for item in result['pubsub']['items']['substanzas']:
print('Retrieved item %s: %s' % (item['id'], tostring(item['payload']))) logging.info('Retrieved item %s: %s', item['id'], tostring(item['payload']))
except: except XMPPError as error:
logging.error('Could not retrieve item %s from node %s' % (self.data, self.node)) 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: try:
self['xep_0060'].retract(self.pubsub_server, self.node, self.data) await self['xep_0060'].retract(self.pubsub_server, self.node, self.data)
print('Retracted item %s from node %s' % (self.data, self.node)) logging.info('Retracted item %s from node %s', self.data, self.node)
except: except XMPPError as error:
logging.error('Could not retract item %s from node %s' % (self.data, self.node)) 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: try:
self['xep_0060'].purge(self.pubsub_server, self.node) await self['xep_0060'].purge(self.pubsub_server, self.node)
print('Purged all items from node %s' % self.node) logging.info('Purged all items from node %s', self.node)
except: except XMPPError as error:
logging.error('Could not purge items from node %s' % self.node) logging.error('Could not purge items from node %s: %s', self.node, error.format())
def subscribe(self): async def subscribe(self):
try: try:
self['xep_0060'].subscribe(self.pubsub_server, self.node) iq = await self['xep_0060'].subscribe(self.pubsub_server, self.node)
print('Subscribed %s to node %s' % (self.boundjid.bare, self.node)) subscription = iq['pubsub']['subscription']
except: logging.info('Subscribed %s to node %s', subscription['jid'], subscription['node'])
logging.error('Could not subscribe %s to node %s' % (self.boundjid.bare, self.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: try:
self['xep_0060'].unsubscribe(self.pubsub_server, self.node) await self['xep_0060'].unsubscribe(self.pubsub_server, self.node)
print('Unsubscribed %s from node %s' % (self.boundjid.bare, self.node)) logging.info('Unsubscribed %s from node %s', self.boundjid.bare, self.node)
except: except XMPPError as error:
logging.error('Could not unsubscribe %s from node %s' % (self.boundjid.bare, self.node)) logging.error('Could not unsubscribe %s from node %s: %s', self.boundjid.bare, self.node, error.format())
@@ -126,19 +123,19 @@ if __name__ == '__main__':
parser = ArgumentParser() parser = ArgumentParser()
parser.version = '%%prog 0.1' parser.version = '%%prog 0.1'
parser.usage = "Usage: %%prog [options] <jid> " + \ 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>]' ' [<node> <data>]'
parser.add_argument("-q","--quiet", help="set logging to ERROR", parser.add_argument("-q","--quiet", help="set logging to ERROR",
action="store_const", action="store_const",
dest="loglevel", dest="loglevel",
const=logging.ERROR, const=logging.ERROR,
default=logging.ERROR) default=logging.INFO)
parser.add_argument("-d","--debug", help="set logging to DEBUG", parser.add_argument("-d","--debug", help="set logging to DEBUG",
action="store_const", action="store_const",
dest="loglevel", dest="loglevel",
const=logging.DEBUG, const=logging.DEBUG,
default=logging.ERROR) default=logging.INFO)
# JID and password options. # JID and password options.
parser.add_argument("-j", "--jid", dest="jid", parser.add_argument("-j", "--jid", dest="jid",
@@ -147,7 +144,7 @@ if __name__ == '__main__':
help="password to use") help="password to use")
parser.add_argument("server") parser.add_argument("server")
parser.add_argument("action", choice=["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("node", nargs='?')
parser.add_argument("data", nargs='?') parser.add_argument("data", nargs='?')
@@ -171,4 +168,4 @@ if __name__ == '__main__':
# Connect to the XMPP server and start processing XMPP stanzas. # Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect() xmpp.connect()
xmpp.process() xmpp.process(forever=False)

View File

@@ -14,7 +14,7 @@ from slixmpp.xmlstream.handler import Callback
class PubsubEvents(slixmpp.ClientXMPP): class PubsubEvents(slixmpp.ClientXMPP):
def __init__(self, jid, password): def __init__(self, jid, password):
super(PubsubEvents, self).__init__(jid, password) super().__init__(jid, password)
self.register_plugin('xep_0030') self.register_plugin('xep_0030')
self.register_plugin('xep_0059') self.register_plugin('xep_0059')

View File

@@ -66,7 +66,7 @@ class RegisterBot(slixmpp.ClientXMPP):
# We're only concerned about registering, so nothing more to do here. # We're only concerned about registering, so nothing more to do here.
self.disconnect() self.disconnect()
def register(self, iq): async def register(self, iq):
""" """
Fill out and submit a registration form. Fill out and submit a registration form.
@@ -90,7 +90,7 @@ class RegisterBot(slixmpp.ClientXMPP):
resp['register']['password'] = self.password resp['register']['password'] = self.password
try: try:
yield from resp.send() await resp.send()
logging.info("Account created for %s!" % self.boundjid) logging.info("Account created for %s!" % self.boundjid)
except IqError as e: except IqError as e:
logging.error("Could not register account: %s" % logging.error("Could not register account: %s" %

View File

@@ -38,8 +38,7 @@ class RosterBrowser(slixmpp.ClientXMPP):
self.received = set() self.received = set()
self.presences_received = asyncio.Event() self.presences_received = asyncio.Event()
@asyncio.coroutine async def start(self, event):
def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -57,16 +56,16 @@ class RosterBrowser(slixmpp.ClientXMPP):
future.set_result(None) future.set_result(None)
try: try:
self.get_roster(callback=callback) self.get_roster(callback=callback)
yield from future await future
except IqError as err: except IqError as err:
print('Error: %' % err.iq['error']['condition']) print('Error: %s' % err.iq['error']['condition'])
except IqTimeout: except IqTimeout:
print('Error: Request timed out') print('Error: Request timed out')
self.send_presence() self.send_presence()
print('Waiting for presence updates...\n') print('Waiting for presence updates...\n')
yield from asyncio.sleep(10) await asyncio.sleep(10)
print('Roster for %s' % self.boundjid.bare) print('Roster for %s' % self.boundjid.bare)
groups = self.client_roster.groups() groups = self.client_roster.groups()

View File

@@ -0,0 +1,89 @@
#!/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
class S5BReceiver(slixmpp.ClientXMPP):
"""
A basic example of creating and using a SOCKS5 bytestream.
"""
def __init__(self, jid, password, filename):
slixmpp.ClientXMPP.__init__(self, jid, password)
self.file = open(filename, 'wb')
self.add_event_handler("socks5_connected", self.stream_opened)
self.add_event_handler("socks5_data", self.stream_data)
self.add_event_handler("socks5_closed", self.stream_closed)
def stream_opened(self, sid):
logging.info('Stream opened. %s', sid)
def stream_data(self, data):
self.file.write(data)
def stream_closed(self, exception):
logging.info('Stream closed. %s', exception)
self.file.close()
self.disconnect()
if __name__ == '__main__':
# Setup the command line arguments.
parser = ArgumentParser()
# Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR",
action="store_const", dest="loglevel",
const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG",
action="store_const", dest="loglevel",
const=logging.DEBUG, default=logging.INFO)
# JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
parser.add_argument("-o", "--out", dest="filename",
help="file to save to")
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input("Username: ")
if args.password is None:
args.password = getpass("Password: ")
if args.filename is None:
args.filename = input("File path: ")
# Setup the S5BReceiver and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = S5BReceiver(args.jid, args.password, args.filename)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0065', {
'auto_accept': True
}) # SOCKS5 Bytestreams
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
xmpp.process(forever=False)

View File

@@ -0,0 +1,122 @@
#!/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 IqError, IqTimeout
class S5BSender(slixmpp.ClientXMPP):
"""
A basic example of creating and using a SOCKS5 bytestream.
"""
def __init__(self, jid, password, receiver, filename):
slixmpp.ClientXMPP.__init__(self, jid, password)
self.receiver = receiver
self.file = open(filename, 'rb')
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use.
self.add_event_handler("session_start", self.start)
async 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.
"""
try:
# Open the S5B stream in which to write to.
proxy = await self['xep_0065'].handshake(self.receiver)
# Send the entire file.
while True:
data = self.file.read(1048576)
if not data:
break
await proxy.write(data)
# And finally close the stream.
proxy.transport.write_eof()
except (IqError, IqTimeout):
print('File transfer errored')
else:
print('File transfer finished')
finally:
self.file.close()
self.disconnect()
if __name__ == '__main__':
# Setup the command line arguments.
parser = ArgumentParser()
# Output verbosity options.
parser.add_argument("-q", "--quiet", help="set logging to ERROR",
action="store_const", dest="loglevel",
const=logging.ERROR, default=logging.INFO)
parser.add_argument("-d", "--debug", help="set logging to DEBUG",
action="store_const", dest="loglevel",
const=logging.DEBUG, default=logging.INFO)
# JID and password options.
parser.add_argument("-j", "--jid", dest="jid",
help="JID to use")
parser.add_argument("-p", "--password", dest="password",
help="password to use")
parser.add_argument("-r", "--receiver", dest="receiver",
help="JID of the receiver")
parser.add_argument("-f", "--file", dest="filename",
help="file to send")
parser.add_argument("-m", "--use-messages", action="store_true",
help="use messages instead of iqs for file transfer")
args = parser.parse_args()
# Setup logging.
logging.basicConfig(level=args.loglevel,
format='%(levelname)-8s %(message)s')
if args.jid is None:
args.jid = input("Username: ")
if args.password is None:
args.password = getpass("Password: ")
if args.receiver is None:
args.receiver = input("Receiver: ")
if args.filename is None:
args.filename = input("File path: ")
# Setup the S5BSender and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = S5BSender(args.jid, args.password, args.receiver, args.filename)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0065') # SOCKS5 Bytestreams
# Connect to the XMPP server and start processing XMPP stanzas.
xmpp.connect()
xmpp.process(forever=False)

View File

@@ -18,7 +18,6 @@ from argparse import ArgumentParser
import slixmpp import slixmpp
from slixmpp.exceptions import XMPPError from slixmpp.exceptions import XMPPError
from slixmpp import asyncio
class AvatarSetter(slixmpp.ClientXMPP): class AvatarSetter(slixmpp.ClientXMPP):
@@ -33,8 +32,7 @@ class AvatarSetter(slixmpp.ClientXMPP):
self.filepath = filepath self.filepath = filepath
@asyncio.coroutine async def start(self, event):
def start(self, event):
""" """
Process the session_start event. Process the session_start event.
@@ -68,20 +66,20 @@ class AvatarSetter(slixmpp.ClientXMPP):
used_xep84 = False used_xep84 = False
print('Publish XEP-0084 avatar data') 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): if isinstance(result, XMPPError):
print('Could not publish XEP-0084 avatar') print('Could not publish XEP-0084 avatar')
else: else:
used_xep84 = True used_xep84 = True
print('Update vCard with avatar') 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): if isinstance(result, XMPPError):
print('Could not set vCard avatar') print('Could not set vCard avatar')
if used_xep84: if used_xep84:
print('Advertise XEP-0084 avatar metadata') 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, {'id': avatar_id,
'type': avatar_type, 'type': avatar_type,
'bytes': avatar_bytes} 'bytes': avatar_bytes}

View File

@@ -22,7 +22,7 @@ from slixmpp import ClientXMPP
class LocationBot(ClientXMPP): class LocationBot(ClientXMPP):
def __init__(self, jid, password): 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('session_start', self.start)
self.add_event_handler('user_location_publish', self.add_event_handler('user_location_publish',

View File

@@ -17,7 +17,7 @@ from slixmpp import ClientXMPP
class TuneBot(ClientXMPP): class TuneBot(ClientXMPP):
def __init__(self, jid, password): def __init__(self, jid, password):
super(TuneBot, self).__init__(jid, password) super().__init__(jid, password)
# Check for the current song every 5 seconds. # Check for the current song every 5 seconds.
self.schedule('Check Current Tune', 5, self._update_tune, repeat=True) self.schedule('Check Current Tune', 5, self._update_tune, repeat=True)

View File

@@ -7,26 +7,20 @@
# This software is licensed as described in the README.rst and LICENSE # This software is licensed as described in the README.rst and LICENSE
# file, which you should have received as part of this distribution. # file, which you should have received as part of this distribution.
import os
from pathlib import Path from pathlib import Path
from subprocess import call, DEVNULL, check_output, CalledProcessError
from tempfile import TemporaryFile
try: try:
from setuptools import setup from setuptools import setup
except ImportError: except ImportError:
from distutils.core import setup 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 run_tests import TestCommand
from slixmpp.version import __version__ from slixmpp.version import __version__
VERSION = __version__ VERSION = __version__
DESCRIPTION = ('Slixmpp is an elegant Python library for XMPP (aka Jabber, ' DESCRIPTION = ('Slixmpp is an elegant Python library for XMPP (aka Jabber).')
'Google Talk, etc).')
with open('README.rst', encoding='utf8') as readme: with open('README.rst', encoding='utf8') as readme:
LONG_DESCRIPTION = readme.read() LONG_DESCRIPTION = readme.read()
@@ -34,12 +28,48 @@ CLASSIFIERS = [
'Intended Audience :: Developers', 'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License', 'License :: OSI Approved :: MIT License',
'Programming Language :: Python', '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', 'Topic :: Software Development :: Libraries :: Python Modules',
] ]
packages = [str(mod.parent) for mod in Path('slixmpp').rglob('__init__.py')] 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( setup(
name="slixmpp", name="slixmpp",
version=VERSION, version=VERSION,
@@ -47,12 +77,12 @@ setup(
long_description=LONG_DESCRIPTION, long_description=LONG_DESCRIPTION,
author='Florent Le Coz', author='Florent Le Coz',
author_email='louiz@louiz.org', author_email='louiz@louiz.org',
url='https://dev.louiz.org/projects/slixmpp', url='https://lab.louiz.org/poezio/slixmpp',
license='MIT', license='MIT',
platforms=['any'], platforms=['any'],
packages=packages, packages=packages,
ext_modules=ext_modules, ext_modules=ext_modules,
requires=['aiodns', 'pyasn1', 'pyasn1_modules'], install_requires=['aiodns>=1.0', 'pyasn1', 'pyasn1_modules', 'aiohttp'],
classifiers=CLASSIFIERS, classifiers=CLASSIFIERS,
cmdclass={'test': TestCommand} cmdclass={'test': TestCommand}
) )

View File

@@ -9,6 +9,10 @@
import logging import logging
logging.getLogger(__name__).addHandler(logging.NullHandler()) 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.stanza import Message, Presence, Iq
from slixmpp.jid import JID, InvalidJID from slixmpp.jid import JID, InvalidJID

View File

@@ -12,8 +12,8 @@
:license: MIT, see LICENSE for more details :license: MIT, see LICENSE for more details
""" """
import asyncio
import logging import logging
import threading
from slixmpp import plugins, roster, stanza from slixmpp import plugins, roster, stanza
from slixmpp.api import APIRegistry from slixmpp.api import APIRegistry
@@ -21,8 +21,6 @@ from slixmpp.exceptions import IqError, IqTimeout
from slixmpp.stanza import Message, Presence, Iq, StreamError from slixmpp.stanza import Message, Presence, Iq, StreamError
from slixmpp.stanza.roster import Roster from slixmpp.stanza.roster import Roster
from slixmpp.stanza.nick import Nick
from slixmpp.stanza.htmlim import HTMLIM
from slixmpp.xmlstream import XMLStream, JID from slixmpp.xmlstream import XMLStream, JID
from slixmpp.xmlstream import ET, register_stanza_plugin from slixmpp.xmlstream import ET, register_stanza_plugin
@@ -46,8 +44,8 @@ class BaseXMPP(XMLStream):
is used during initialization. is used during initialization.
""" """
def __init__(self, jid='', default_ns='jabber:client'): def __init__(self, jid='', default_ns='jabber:client', **kwargs):
XMLStream.__init__(self) XMLStream.__init__(self, **kwargs)
self.default_ns = default_ns self.default_ns = default_ns
self.stream_ns = 'http://etherx.jabber.org/streams' self.stream_ns = 'http://etherx.jabber.org/streams'
@@ -71,7 +69,7 @@ class BaseXMPP(XMLStream):
#: redirections that will be followed before quitting. #: redirections that will be followed before quitting.
self.max_redirects = 5 self.max_redirects = 5
self.session_bind_event = threading.Event() self.session_bind_event = asyncio.Event()
#: A dictionary mapping plugin names to plugins. #: A dictionary mapping plugin names to plugins.
self.plugin = PluginManager(self) self.plugin = PluginManager(self)
@@ -106,12 +104,15 @@ class BaseXMPP(XMLStream):
#: :attr:`use_message_ids` to `True` will assign all outgoing #: :attr:`use_message_ids` to `True` will assign all outgoing
#: messages an ID. Some plugin features require enabling #: messages an ID. Some plugin features require enabling
#: this option. #: this option.
self.use_message_ids = False self.use_message_ids = True
#: Presence updates may optionally be tagged with ID values. #: Presence updates may optionally be tagged with ID values.
#: Setting :attr:`use_message_ids` to `True` will assign all #: Setting :attr:`use_message_ids` to `True` will assign all
#: outgoing messages an ID. #: 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 #: The API registry is a way to process callbacks based on
#: JID+node combinations. Each callback in the registry is #: JID+node combinations. Each callback in the registry is
@@ -195,7 +196,6 @@ class BaseXMPP(XMLStream):
# Initialize a few default stanza plugins. # Initialize a few default stanza plugins.
register_stanza_plugin(Iq, Roster) register_stanza_plugin(Iq, Roster)
register_stanza_plugin(Message, Nick)
def start_stream_handler(self, xml): def start_stream_handler(self, xml):
"""Save the stream ID once the streams have been established. """Save the stream ID once the streams have been established.
@@ -221,7 +221,7 @@ class BaseXMPP(XMLStream):
self.plugin[name].post_init() self.plugin[name].post_init()
self.plugin[name].post_inited = True self.plugin[name].post_inited = True
def register_plugin(self, plugin, pconfig={}, module=None): def register_plugin(self, plugin, pconfig=None, module=None):
"""Register and configure a plugin for use in this stream. """Register and configure a plugin for use in this stream.
:param plugin: The name of the plugin class. Plugin names must :param plugin: The name of the plugin class. Plugin names must
@@ -688,7 +688,6 @@ class BaseXMPP(XMLStream):
self.address = (host, port) self.address = (host, port)
self.default_domain = host self.default_domain = host
self.dns_records = None self.dns_records = None
self.reconnect_delay = None
self.reconnect() self.reconnect()
def _handle_message(self, msg): def _handle_message(self, msg):
@@ -754,6 +753,9 @@ class BaseXMPP(XMLStream):
Update the roster with presence information. Update the roster with presence information.
""" """
if self.roster[presence['from']].ignore_updates:
return
if not self.is_component and not presence['to'].bare: if not self.is_component and not presence['to'].bare:
presence['to'] = self.boundjid presence['to'] = self.boundjid

View File

@@ -12,14 +12,16 @@
:license: MIT, see LICENSE for more details :license: MIT, see LICENSE for more details
""" """
import asyncio
import logging import logging
from slixmpp.jid import JID
from slixmpp.stanza import StreamFeatures from slixmpp.stanza import StreamFeatures
from slixmpp.basexmpp import BaseXMPP from slixmpp.basexmpp import BaseXMPP
from slixmpp.exceptions import XMPPError from slixmpp.exceptions import XMPPError
from slixmpp.xmlstream import XMLStream from slixmpp.xmlstream import XMLStream
from slixmpp.xmlstream.matcher import StanzaPath, MatchXPath 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. # Flag indicating if DNS SRV records are available for use.
try: try:
@@ -50,7 +52,6 @@ class ClientXMPP(BaseXMPP):
:param jid: The JID of the XMPP user account. :param jid: The JID of the XMPP user account.
:param password: The password for the XMPP user account. :param password: The password for the XMPP user account.
:param ssl: **Deprecated.**
:param plugin_config: A dictionary of plugin configurations. :param plugin_config: A dictionary of plugin configurations.
:param plugin_whitelist: A list of approved plugins that :param plugin_whitelist: A list of approved plugins that
will be loaded when calling will be loaded when calling
@@ -58,9 +59,15 @@ class ClientXMPP(BaseXMPP):
:param escape_quotes: **Deprecated.** :param escape_quotes: **Deprecated.**
""" """
def __init__(self, jid, password, plugin_config={}, plugin_whitelist=[], def __init__(self, jid, password, plugin_config=None,
escape_quotes=True, sasl_mech=None, lang='en'): plugin_whitelist=None, escape_quotes=True, sasl_mech=None,
BaseXMPP.__init__(self, jid, 'jabber:client') lang='en', **kwargs):
if not plugin_whitelist:
plugin_whitelist = []
if not plugin_config:
plugin_config = {}
BaseXMPP.__init__(self, jid, 'jabber:client', **kwargs)
self.escape_quotes = escape_quotes self.escape_quotes = escape_quotes
self.plugin_config = plugin_config self.plugin_config = plugin_config
@@ -99,13 +106,24 @@ class ClientXMPP(BaseXMPP):
self.register_stanza(StreamFeatures) self.register_stanza(StreamFeatures)
self.register_handler( self.register_handler(
Callback('Stream Features', CoroutineCallback('Stream Features',
MatchXPath('{%s}features' % self.stream_ns), MatchXPath('{%s}features' % self.stream_ns),
self._handle_stream_features)) 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( self.register_handler(
Callback('Roster Update', Callback('Roster Update',
StanzaPath('iq@type=set/roster'), StanzaPath('iq@type=set/roster'),
lambda iq: self.event('roster_update', iq))) roster_push_filter))
# Setup default stream features # Setup default stream features
self.register_plugin('feature_starttls') self.register_plugin('feature_starttls')
@@ -135,8 +153,11 @@ class ClientXMPP(BaseXMPP):
will be used. will be used.
:param address: A tuple containing the server's host and port. :param address: A tuple containing the server's host and port.
:param use_tls: Indicates if TLS should be used for the :param force_starttls: Indicates that negotiation should be aborted
connection. Defaults to ``True``. 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 :param use_ssl: Indicates if the older SSL connection method
should be used. Defaults to ``False``. should be used. Defaults to ``False``.
""" """
@@ -234,7 +255,7 @@ class ClientXMPP(BaseXMPP):
orig_cb(resp) orig_cb(resp)
callback = wrapped callback = wrapped
iq.send(callback, timeout, timeout_callback) return iq.send(callback, timeout, timeout_callback)
def _reset_connection_state(self, event=None): def _reset_connection_state(self, event=None):
#TODO: Use stream state here #TODO: Use stream state here
@@ -244,7 +265,7 @@ class ClientXMPP(BaseXMPP):
self.bindfail = False self.bindfail = False
self.features = set() self.features = set()
def _handle_stream_features(self, features): async def _handle_stream_features(self, features):
"""Process the received stream features. """Process the received stream features.
:param features: The features stanza. :param features: The features stanza.
@@ -252,7 +273,11 @@ class ClientXMPP(BaseXMPP):
for order, name in self._stream_feature_order: for order, name in self._stream_feature_order:
if name in features['features']: if name in features['features']:
handler, restart = self._stream_feature_handlers[name] 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 # Don't continue if the feature requires
# restarting the XML stream. # restarting the XML stream.
return True return True

View File

@@ -46,8 +46,13 @@ class ComponentXMPP(BaseXMPP):
Defaults to ``False``. Defaults to ``False``.
""" """
def __init__(self, jid, secret, host=None, port=None, def __init__(self, jid, secret, host=None, port=None, plugin_config=None, plugin_whitelist=None, use_jc_ns=False):
plugin_config={}, plugin_whitelist=[], use_jc_ns=False):
if not plugin_whitelist:
plugin_whitelist = []
if not plugin_config:
plugin_config = {}
if use_jc_ns: if use_jc_ns:
default_ns = 'jabber:client' default_ns = 'jabber:client'
else: else:

View File

@@ -56,6 +56,18 @@ class XMPPError(Exception):
self.extension_ns = extension_ns self.extension_ns = extension_ns
self.extension_args = extension_args self.extension_args = extension_args
def format(self):
"""
Format the error in a simple user-readable string.
"""
text = [self.etype, self.condition]
if self.text:
text.append(self.text)
if self.extension:
text.append(self.extension)
# TODO: handle self.extension_args
return ': '.join(text)
class IqTimeout(XMPPError): class IqTimeout(XMPPError):
@@ -65,7 +77,7 @@ class IqTimeout(XMPPError):
""" """
def __init__(self, iq): def __init__(self, iq):
super(IqTimeout, self).__init__( super().__init__(
condition='remote-server-timeout', condition='remote-server-timeout',
etype='cancel') etype='cancel')
@@ -82,7 +94,7 @@ class IqError(XMPPError):
""" """
def __init__(self, iq): def __init__(self, iq):
super(IqError, self).__init__( super().__init__(
condition=iq['error']['condition'], condition=iq['error']['condition'],
text=iq['error']['text'], text=iq['error']['text'],
etype=iq['error']['type']) etype=iq['error']['type'])

View File

@@ -13,7 +13,3 @@ from slixmpp.features.feature_bind.stanza import Bind
register_plugin(FeatureBind) register_plugin(FeatureBind)
# Retain some backwards compatibility
feature_bind = FeatureBind

View File

@@ -6,6 +6,7 @@
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import asyncio
import logging import logging
from slixmpp.jid import JID from slixmpp.jid import JID
@@ -34,7 +35,7 @@ class FeatureBind(BasePlugin):
register_stanza_plugin(Iq, stanza.Bind) register_stanza_plugin(Iq, stanza.Bind)
register_stanza_plugin(StreamFeatures, 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. Handle requesting a specific resource.
@@ -49,7 +50,7 @@ class FeatureBind(BasePlugin):
if self.xmpp.requested_jid.resource: if self.xmpp.requested_jid.resource:
iq['bind']['resource'] = 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): def _on_bind_response(self, response):
self.xmpp.boundjid = JID(response['bind']['jid']) self.xmpp.boundjid = JID(response['bind']['jid'])

View File

@@ -16,6 +16,6 @@ class Bind(ElementBase):
name = 'bind' name = 'bind'
namespace = 'urn:ietf:params:xml:ns:xmpp-bind' namespace = 'urn:ietf:params:xml:ns:xmpp-bind'
interfaces = set(('resource', 'jid')) interfaces = {'resource', 'jid'}
sub_interfaces = interfaces sub_interfaces = interfaces
plugin_attrib = 'bind' plugin_attrib = 'bind'

View File

@@ -16,7 +16,3 @@ from slixmpp.features.feature_mechanisms.stanza import Failure
register_plugin(FeatureMechanisms) register_plugin(FeatureMechanisms)
# Retain some backwards compatibility
feature_mechanisms = FeatureMechanisms

View File

@@ -49,7 +49,7 @@ class FeatureMechanisms(BasePlugin):
if self.security_callback is None: if self.security_callback is None:
self.security_callback = self._default_security 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']: if not self.use_mech and not creds['username']:
self.use_mech = 'ANONYMOUS' self.use_mech = 'ANONYMOUS'
@@ -97,12 +97,9 @@ class FeatureMechanisms(BasePlugin):
jid = self.xmpp.requested_jid.bare jid = self.xmpp.requested_jid.bare
result[value] = creds.get('email', jid) result[value] = creds.get('email', jid)
elif value == 'channel_binding': 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() result[value] = self.xmpp.socket.get_channel_binding()
else: 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 result[value] = None
elif value == 'host': elif value == 'host':
result[value] = creds.get('host', self.xmpp.requested_jid.domain) result[value] = creds.get('host', self.xmpp.requested_jid.domain)
@@ -122,7 +119,7 @@ class FeatureMechanisms(BasePlugin):
if value == 'encrypted': if value == 'encrypted':
if 'starttls' in self.xmpp.features: if 'starttls' in self.xmpp.features:
result[value] = True result[value] = True
elif isinstance(self.xmpp.socket, ssl.SSLSocket): elif isinstance(self.xmpp.socket, (ssl.SSLSocket, ssl.SSLObject)):
result[value] = True result[value] = True
else: else:
result[value] = False result[value] = False
@@ -190,14 +187,14 @@ class FeatureMechanisms(BasePlugin):
except sasl.SASLCancelled: except sasl.SASLCancelled:
self.attempted_mechs.add(self.mech.name) self.attempted_mechs.add(self.mech.name)
self._send_auth() self._send_auth()
except sasl.SASLFailed:
self.attempted_mechs.add(self.mech.name)
self._send_auth()
except sasl.SASLMutualAuthFailed: except sasl.SASLMutualAuthFailed:
log.error("Mutual authentication failed! " + \ log.error("Mutual authentication failed! " + \
"A security breach is possible.") "A security breach is possible.")
self.attempted_mechs.add(self.mech.name) self.attempted_mechs.add(self.mech.name)
self.xmpp.disconnect() self.xmpp.disconnect()
except sasl.SASLFailed:
self.attempted_mechs.add(self.mech.name)
self._send_auth()
else: else:
resp.send() resp.send()
@@ -210,13 +207,13 @@ class FeatureMechanisms(BasePlugin):
resp['value'] = self.mech.process(stanza['value']) resp['value'] = self.mech.process(stanza['value'])
except sasl.SASLCancelled: except sasl.SASLCancelled:
self.stanza.Abort(self.xmpp).send() self.stanza.Abort(self.xmpp).send()
except sasl.SASLFailed:
self.stanza.Abort(self.xmpp).send()
except sasl.SASLMutualAuthFailed: except sasl.SASLMutualAuthFailed:
log.error("Mutual authentication failed! " + \ log.error("Mutual authentication failed! " + \
"A security breach is possible.") "A security breach is possible.")
self.attempted_mechs.add(self.mech.name) self.attempted_mechs.add(self.mech.name)
self.xmpp.disconnect() self.xmpp.disconnect()
except sasl.SASLFailed:
self.stanza.Abort(self.xmpp).send()
else: else:
if resp.get_value() == '': if resp.get_value() == '':
resp.del_value() resp.del_value()

View File

@@ -19,12 +19,12 @@ class Auth(StanzaBase):
name = 'auth' name = 'auth'
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
interfaces = set(('mechanism', 'value')) interfaces = {'mechanism', 'value'}
plugin_attrib = name plugin_attrib = name
#: Some SASL mechs require sending values as is, #: Some SASL mechs require sending values as is,
#: without converting base64. #: without converting base64.
plain_mechs = set(['X-MESSENGER-OAUTH2']) plain_mechs = {'X-MESSENGER-OAUTH2'}
def setup(self, xml): def setup(self, xml):
StanzaBase.setup(self, xml) StanzaBase.setup(self, xml)

View File

@@ -19,7 +19,7 @@ class Challenge(StanzaBase):
name = 'challenge' name = 'challenge'
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
interfaces = set(('value',)) interfaces = {'value'}
plugin_attrib = name plugin_attrib = name
def setup(self, xml): def setup(self, xml):

View File

@@ -16,13 +16,14 @@ class Failure(StanzaBase):
name = 'failure' name = 'failure'
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
interfaces = set(('condition', 'text')) interfaces = {'condition', 'text'}
plugin_attrib = name plugin_attrib = name
sub_interfaces = set(('text',)) sub_interfaces = {'text'}
conditions = set(('aborted', 'account-disabled', 'credentials-expired', conditions = {'aborted', 'account-disabled', 'credentials-expired',
'encryption-required', 'incorrect-encoding', 'invalid-authzid', 'encryption-required', 'incorrect-encoding',
'invalid-mechanism', 'malformed-request', 'mechansism-too-weak', 'invalid-authzid', 'invalid-mechanism', 'malformed-request',
'not-authorized', 'temporary-auth-failure')) 'mechansism-too-weak', 'not-authorized',
'temporary-auth-failure'}
def setup(self, xml=None): def setup(self, xml=None):
""" """

View File

@@ -16,7 +16,7 @@ class Mechanisms(ElementBase):
name = 'mechanisms' name = 'mechanisms'
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
interfaces = set(('mechanisms', 'required')) interfaces = {'mechanisms', 'required'}
plugin_attrib = name plugin_attrib = name
is_extension = True is_extension = True
@@ -29,7 +29,7 @@ class Mechanisms(ElementBase):
""" """
""" """
results = [] results = []
mechs = self.findall('{%s}mechanism' % self.namespace) mechs = self.xml.findall('{%s}mechanism' % self.namespace)
if mechs: if mechs:
for mech in mechs: for mech in mechs:
results.append(mech.text) results.append(mech.text)
@@ -47,7 +47,7 @@ class Mechanisms(ElementBase):
def del_mechanisms(self): def del_mechanisms(self):
""" """
""" """
mechs = self.findall('{%s}mechanism' % self.namespace) mechs = self.xml.findall('{%s}mechanism' % self.namespace)
if mechs: if mechs:
for mech in mechs: for mech in mechs:
self.xml.remove(mech) self.xml.remove(mech)

View File

@@ -19,7 +19,7 @@ class Response(StanzaBase):
name = 'response' name = 'response'
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
interfaces = set(('value',)) interfaces = {'value'}
plugin_attrib = name plugin_attrib = name
def setup(self, xml): def setup(self, xml):

View File

@@ -18,7 +18,7 @@ class Success(StanzaBase):
name = 'success' name = 'success'
namespace = 'urn:ietf:params:xml:ns:xmpp-sasl' namespace = 'urn:ietf:params:xml:ns:xmpp-sasl'
interfaces = set(['value']) interfaces = {'value'}
plugin_attrib = name plugin_attrib = name
def setup(self, xml): def setup(self, xml):

View File

@@ -13,7 +13,3 @@ from slixmpp.features.feature_rosterver.stanza import RosterVer
register_plugin(FeatureRosterVer) register_plugin(FeatureRosterVer)
# Retain some backwards compatibility
feature_rosterver = FeatureRosterVer

View File

@@ -13,7 +13,3 @@ from slixmpp.features.feature_session.stanza import Session
register_plugin(FeatureSession) register_plugin(FeatureSession)
# Retain some backwards compatibility
feature_session = FeatureSession

View File

@@ -6,6 +6,7 @@
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import asyncio
import logging import logging
from slixmpp.stanza import Iq, StreamFeatures from slixmpp.stanza import Iq, StreamFeatures
@@ -34,17 +35,22 @@ class FeatureSession(BasePlugin):
register_stanza_plugin(Iq, stanza.Session) register_stanza_plugin(Iq, stanza.Session)
register_stanza_plugin(StreamFeatures, 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. Handle the start of the session.
Arguments: Arguments:
feature -- The stream features element. feature -- The stream features element.
""" """
if features['session']['optional']:
self.xmpp.sessionstarted = True
self.xmpp.event('session_start')
return
iq = self.xmpp.Iq() iq = self.xmpp.Iq()
iq['type'] = 'set' iq['type'] = 'set'
iq.enable('session') 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): def _on_start_session_response(self, response):
self.xmpp.features.add('session') self.xmpp.features.add('session')

View File

@@ -6,7 +6,7 @@
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
from slixmpp.xmlstream import ElementBase from slixmpp.xmlstream import ElementBase, ET
class Session(ElementBase): class Session(ElementBase):
@@ -16,5 +16,19 @@ class Session(ElementBase):
name = 'session' name = 'session'
namespace = 'urn:ietf:params:xml:ns:xmpp-session' namespace = 'urn:ietf:params:xml:ns:xmpp-session'
interfaces = set() interfaces = {'optional'}
plugin_attrib = 'session' 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)

View File

@@ -13,7 +13,3 @@ from slixmpp.features.feature_starttls.stanza import *
register_plugin(FeatureSTARTTLS) register_plugin(FeatureSTARTTLS)
# Retain some backwards compatibility
feature_starttls = FeatureSTARTTLS

View File

@@ -16,7 +16,7 @@ class STARTTLS(ElementBase):
name = 'starttls' name = 'starttls'
namespace = 'urn:ietf:params:xml:ns:xmpp-tls' namespace = 'urn:ietf:params:xml:ns:xmpp-tls'
interfaces = set(('required',)) interfaces = {'required'}
plugin_attrib = name plugin_attrib = name
def get_required(self): def get_required(self):

View File

@@ -12,7 +12,7 @@ from slixmpp.stanza import StreamFeatures
from slixmpp.xmlstream import register_stanza_plugin from slixmpp.xmlstream import register_stanza_plugin
from slixmpp.plugins import BasePlugin from slixmpp.plugins import BasePlugin
from slixmpp.xmlstream.matcher import MatchXPath 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 from slixmpp.features.feature_starttls import stanza
@@ -28,7 +28,7 @@ class FeatureSTARTTLS(BasePlugin):
def plugin_init(self): def plugin_init(self):
self.xmpp.register_handler( self.xmpp.register_handler(
Callback('STARTTLS Proceed', CoroutineCallback('STARTTLS Proceed',
MatchXPath(stanza.Proceed.tag_name()), MatchXPath(stanza.Proceed.tag_name()),
self._handle_starttls_proceed, self._handle_starttls_proceed,
instream=True)) instream=True))
@@ -58,8 +58,8 @@ class FeatureSTARTTLS(BasePlugin):
self.xmpp.send(features['starttls']) self.xmpp.send(features['starttls'])
return True return True
def _handle_starttls_proceed(self, proceed): async def _handle_starttls_proceed(self, proceed):
"""Restart the XML stream when TLS is accepted.""" """Restart the XML stream when TLS is accepted."""
log.debug("Starting TLS") log.debug("Starting TLS")
if self.xmpp.start_tls(): if await self.xmpp.start_tls():
self.xmpp.features.add('starttls') self.xmpp.features.add('starttls')

View File

@@ -16,6 +16,7 @@ import socket
from copy import deepcopy from copy import deepcopy
from functools import lru_cache from functools import lru_cache
from typing import Optional
from slixmpp.stringprep import nodeprep, resourceprep, idna, StringprepError from slixmpp.stringprep import nodeprep, resourceprep, idna, StringprepError
@@ -71,7 +72,7 @@ def _parse_jid(data):
return node, domain, resource return node, domain, resource
def _validate_node(node): def _validate_node(node: Optional[str]):
"""Validate the local, or username, portion of a JID. """Validate the local, or username, portion of a JID.
:raises InvalidJID: :raises InvalidJID:
@@ -79,7 +80,7 @@ def _validate_node(node):
:returns: The local portion of a JID, as validated by nodeprep. :returns: The local portion of a JID, as validated by nodeprep.
""" """
if node is None: if node is None:
return None return ''
try: try:
node = nodeprep(node) node = nodeprep(node)
@@ -93,7 +94,7 @@ def _validate_node(node):
return node return node
def _validate_domain(domain): def _validate_domain(domain: str):
"""Validate the domain portion of a JID. """Validate the domain portion of a JID.
IP literal addresses are left as-is, if valid. Domain names IP literal addresses are left as-is, if valid. Domain names
@@ -152,7 +153,7 @@ def _validate_domain(domain):
return domain return domain
def _validate_resource(resource): def _validate_resource(resource: Optional[str]):
"""Validate the resource portion of a JID. """Validate the resource portion of a JID.
:raises InvalidJID: :raises InvalidJID:
@@ -160,7 +161,7 @@ def _validate_resource(resource):
:returns: The local portion of a JID, as validated by resourceprep. :returns: The local portion of a JID, as validated by resourceprep.
""" """
if resource is None: if resource is None:
return None return ''
try: try:
resource = resourceprep(resource) resource = resourceprep(resource)
@@ -174,7 +175,7 @@ def _validate_resource(resource):
return resource return resource
def _unescape_node(node): def _unescape_node(node: str):
"""Unescape a local portion of a JID. """Unescape a local portion of a JID.
.. note:: .. note::
@@ -199,7 +200,11 @@ def _unescape_node(node):
return ''.join(unescaped) 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. """Format the given JID components into a full or bare JID.
:param string local: Optional. The local portion of the 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. :return: A full or bare JID string.
""" """
result = [] if domain is None:
return ''
if local is not None: if local is not None:
result.append(local) result = local + '@' + domain
result.append('@') else:
if domain is not None: result = domain
result.append(domain)
if resource is not None: if resource is not None:
result.append('/') result += '/' + resource
result.append(resource) return result
return ''.join(result)
class InvalidJID(ValueError): class InvalidJID(ValueError):
@@ -238,12 +242,17 @@ class UnescapedJID:
__slots__ = ('_node', '_domain', '_resource') __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._node = node
self._domain = domain self._domain = domain
self._resource = resource self._resource = resource
def __getattribute__(self, name): def __getattribute__(self, name: str):
"""Retrieve the given JID component. """Retrieve the given JID component.
:param name: one of: user, server, domain, resource, :param name: one of: user, server, domain, resource,
@@ -300,19 +309,23 @@ class JID:
:raises InvalidJID: :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: if not jid:
self._node = None self._node = ''
self._domain = None self._domain = ''
self._resource = None self._resource = ''
self._bare = ''
self._full = ''
return
elif not isinstance(jid, JID): elif not isinstance(jid, JID):
self._node, self._domain, self._resource = _parse_jid(jid) self._node, self._domain, self._resource = _parse_jid(jid)
else: else:
self._node = jid._node self._node = jid._node
self._domain = jid._domain self._domain = jid._domain
self._resource = jid._resource self._resource = jid._resource
self._update_bare_full()
def unescape(self): def unescape(self):
"""Return an unescaped JID object. """Return an unescaped JID object.
@@ -329,104 +342,80 @@ class JID:
self._domain, self._domain,
self._resource) 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 @property
def node(self): def node(self):
return self._node or '' return self._node
@property
def user(self):
return self._node or ''
@property
def local(self):
return self._node or ''
@property
def username(self):
return self._node or ''
@property @property
def domain(self): def domain(self):
return self._domain or '' return self._domain
@property
def server(self):
return self._domain or ''
@property
def host(self):
return self._domain or ''
@property @property
def resource(self): def resource(self):
return self._resource or '' return self._resource
@property @property
def bare(self): def bare(self):
return _format_jid(self._node, self._domain) return self._bare
@property @property
def full(self): def full(self):
return _format_jid(self._node, self._domain, self._resource) return self._full
@property
def jid(self):
return _format_jid(self._node, self._domain, self._resource)
@node.setter @node.setter
def node(self, value): def node(self, value: str):
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):
self._node = _validate_node(value) self._node = _validate_node(value)
self._update_bare_full()
@domain.setter @domain.setter
def domain(self, value): def domain(self, value: str):
self._domain = _validate_domain(value)
@server.setter
def server(self, value):
self._domain = _validate_domain(value)
@host.setter
def host(self, value):
self._domain = _validate_domain(value) self._domain = _validate_domain(value)
self._update_bare_full()
@bare.setter @bare.setter
def bare(self, value): def bare(self, value: str):
node, domain, resource = _parse_jid(value) node, domain, resource = _parse_jid(value)
assert not resource assert not resource
self._node = node self._node = node
self._domain = domain self._domain = domain
self._update_bare_full()
@resource.setter @resource.setter
def resource(self, value): def resource(self, value: str):
self._resource = _validate_resource(value) self._resource = _validate_resource(value)
self._update_bare_full()
@full.setter @full.setter
def full(self, value): def full(self, value: str):
self._node, self._domain, self._resource = _parse_jid(value) self._node, self._domain, self._resource = _parse_jid(value)
self._update_bare_full()
@jid.setter user = node
def jid(self, value): local = node
self._node, self._domain, self._resource = _parse_jid(value) username = node
server = domain
host = domain
jid = full
def __str__(self): def __str__(self):
"""Use the full JID as the string value.""" """Use the full JID as the string value."""
return _format_jid(self._node, self._domain, self._resource) return self._full
def __repr__(self): def __repr__(self):
"""Use the full JID as the representation.""" """Use the full JID as the representation."""
return _format_jid(self._node, self._domain, self._resource) return self._full
# pylint: disable=W0212 # pylint: disable=W0212
def __eq__(self, other): def __eq__(self, other):
@@ -434,7 +423,10 @@ class JID:
if isinstance(other, UnescapedJID): if isinstance(other, UnescapedJID):
return False return False
if not isinstance(other, JID): if not isinstance(other, JID):
other = JID(other) try:
other = JID(other)
except InvalidJID:
return NotImplemented
return (self._node == other._node and return (self._node == other._node and
self._domain == other._domain and self._domain == other._domain and
@@ -446,4 +438,4 @@ class JID:
def __hash__(self): def __hash__(self):
"""Hash a JID based on the string version of its full JID.""" """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)

View File

@@ -47,6 +47,7 @@ __all__ = [
'xep_0108', # User Activity 'xep_0108', # User Activity
'xep_0115', # Entity Capabilities 'xep_0115', # Entity Capabilities
'xep_0118', # User Tune 'xep_0118', # User Tune
'xep_0122', # Data Forms Validation
'xep_0128', # Extended Service Discovery 'xep_0128', # Extended Service Discovery
'xep_0131', # Standard Headers and Internet Metadata 'xep_0131', # Standard Headers and Internet Metadata
'xep_0133', # Service Administration 'xep_0133', # Service Administration
@@ -83,4 +84,7 @@ __all__ = [
'xep_0319', # Last User Interaction in Presence 'xep_0319', # Last User Interaction in Presence
'xep_0323', # IoT Systems Sensor Data 'xep_0323', # IoT Systems Sensor Data
'xep_0325', # IoT Systems Control '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
] ]

View File

@@ -308,7 +308,7 @@ class BasePlugin(object):
if key in self.default_config: if key in self.default_config:
self.config[key] = value self.config[key] = value
else: else:
super(BasePlugin, self).__setattr__(key, value) super().__setattr__(key, value)
def _init(self): def _init(self):
"""Initialize plugin state, such as registering event handlers. """Initialize plugin state, such as registering event handlers.

View File

@@ -21,15 +21,15 @@ class GmailQuery(ElementBase):
namespace = 'google:mail:notify' namespace = 'google:mail:notify'
name = 'query' name = 'query'
plugin_attrib = 'gmail' 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'] return self['q']
def setSearch(self, search): def set_search(self, search):
self['q'] = search self['q'] = search
def delSearch(self): def del_search(self):
del self['q'] del self['q']
@@ -37,20 +37,20 @@ class MailBox(ElementBase):
namespace = 'google:mail:notify' namespace = 'google:mail:notify'
name = 'mailbox' name = 'mailbox'
plugin_attrib = 'mailbox' plugin_attrib = 'mailbox'
interfaces = set(('result-time', 'total-matched', 'total-estimate', interfaces = {'result-time', 'total-matched', 'total-estimate',
'url', 'threads', 'matched', 'estimate')) 'url', 'threads', 'matched', 'estimate'}
def getThreads(self): def get_threads(self):
threads = [] threads = []
for threadXML in self.xml.findall('{%s}%s' % (MailThread.namespace, for threadXML in self.xml.findall('{%s}%s' % (MailThread.namespace,
MailThread.name)): MailThread.name)):
threads.append(MailThread(xml=threadXML, parent=None)) threads.append(MailThread(xml=threadXML, parent=None))
return threads return threads
def getMatched(self): def get_matched(self):
return self['total-matched'] return self['total-matched']
def getEstimate(self): def get_estimate(self):
return self['total-estimate'] == '1' return self['total-estimate'] == '1'
@@ -58,11 +58,11 @@ class MailThread(ElementBase):
namespace = 'google:mail:notify' namespace = 'google:mail:notify'
name = 'mail-thread-info' name = 'mail-thread-info'
plugin_attrib = 'thread' plugin_attrib = 'thread'
interfaces = set(('tid', 'participation', 'messages', 'date', interfaces = {'tid', 'participation', 'messages', 'date',
'senders', 'url', 'labels', 'subject', 'snippet')) 'senders', 'url', 'labels', 'subject', 'snippet'}
sub_interfaces = set(('labels', 'subject', 'snippet')) sub_interfaces = {'labels', 'subject', 'snippet'}
def getSenders(self): def get_senders(self):
senders = [] senders = []
sendersXML = self.xml.find('{%s}senders' % self.namespace) sendersXML = self.xml.find('{%s}senders' % self.namespace)
if sendersXML is not None: if sendersXML is not None:
@@ -75,12 +75,12 @@ class MailSender(ElementBase):
namespace = 'google:mail:notify' namespace = 'google:mail:notify'
name = 'sender' name = 'sender'
plugin_attrib = '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' return self.xml.attrib.get('originator', '0') == '1'
def getUnread(self): def get_unread(self):
return self.xml.attrib.get('unread', '0') == '1' return self.xml.attrib.get('unread', '0') == '1'

View 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)

View 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

View 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

View 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 ElementBase, ET
class GoogleAuth(ElementBase):
name = 'auth'
namespace = 'http://www.google.com/talk/protocol/auth'
plugin_attrib = 'google'
interfaces = {'client_uses_full_bind_result', 'service'}
discovery_attr= '{%s}client-uses-full-bind-result' % namespace
service_attr= '{%s}service' % namespace
def setup(self, xml):
"""Don't create XML for the plugin."""
self.xml = ET.Element('')
def get_client_uses_full_bind_result(self):
return self.parent()._get_attr(self.discovery_attr) == 'true'
def set_client_uses_full_bind_result(self, value):
if value in (True, 'true'):
self.parent()._set_attr(self.discovery_attr, 'true')
else:
self.parent()._del_attr(self.discovery_attr)
def del_client_uses_full_bind_result(self):
self.parent()._del_attr(self.discovery_attr)
def get_service(self):
return self.parent()._get_attr(self.service_attr, '')
def set_service(self, value):
if value:
self.parent()._set_attr(self.service_attr, value)
else:
self.parent()._del_attr(self.service_attr)
def del_service(self):
self.parent()._del_attr(self.service_attr)

View 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

View File

@@ -0,0 +1,90 @@
"""
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.
"""
import logging
from slixmpp.stanza import Iq
from slixmpp.xmlstream.handler import Callback
from slixmpp.xmlstream.matcher import MatchXPath
from slixmpp.xmlstream import register_stanza_plugin
from slixmpp.plugins import BasePlugin
from slixmpp.plugins.google.gmail import stanza
log = logging.getLogger(__name__)
class Gmail(BasePlugin):
"""
Google: Gmail Notifications
Also see <https://developers.google.com/talk/jep_extensions/gmail>.
"""
name = 'gmail'
description = 'Google: Gmail Notifications'
dependencies = set()
stanza = stanza
def plugin_init(self):
register_stanza_plugin(Iq, stanza.GmailQuery)
register_stanza_plugin(Iq, stanza.MailBox)
register_stanza_plugin(Iq, stanza.NewMail)
self.xmpp.register_handler(
Callback('Gmail New Mail',
MatchXPath('{%s}iq/{%s}%s' % (
self.xmpp.default_ns,
stanza.NewMail.namespace,
stanza.NewMail.name)),
self._handle_new_mail))
self._last_result_time = None
self._last_result_tid = None
def plugin_end(self):
self.xmpp.remove_handler('Gmail New Mail')
def _handle_new_mail(self, iq):
log.info('Gmail: New email!')
iq.reply().send()
self.xmpp.event('gmail_notification')
def check(self, timeout=None, callback=None):
last_time = self._last_result_time
last_tid = self._last_result_tid
callback = lambda iq: self._update_last_results(iq, callback)
return self.search(newer_time=last_time,
newer_tid=last_tid,
timeout=timeout,
callback=callback)
def _update_last_results(self, iq, callback=None):
self._last_result_time = iq['gmail_messages']['result_time']
threads = iq['gmail_messages']['threads']
if threads:
self._last_result_tid = threads[0]['tid']
if callback:
callback(iq)
def search(self, query=None, newer_time=None, newer_tid=None,
timeout=None, callback=None):
if not query:
log.info('Gmail: Checking for new email')
else:
log.info('Gmail: Searching for emails matching: "%s"', query)
iq = self.xmpp.Iq()
iq['type'] = 'get'
iq['to'] = self.xmpp.boundjid.bare
iq['gmail']['search'] = query
iq['gmail']['newer_than_time'] = newer_time
iq['gmail']['newer_than_tid'] = newer_tid
return iq.send(timeout=timeout, callback=callback)

View 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)

View 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

View 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)

View File

@@ -0,0 +1,59 @@
"""
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.jid import JID
from slixmpp.xmlstream import ElementBase, register_stanza_plugin
class NoSave(ElementBase):
name = 'x'
namespace = 'google:nosave'
plugin_attrib = 'google_nosave'
interfaces = {'value'}
def get_value(self):
return self._get_attr('value', '') == 'enabled'
def set_value(self, value):
self._set_attr('value', 'enabled' if value else 'disabled')
class NoSaveQuery(ElementBase):
name = 'query'
namespace = 'google:nosave'
plugin_attrib = 'google_nosave'
interfaces = set()
class Item(ElementBase):
name = 'item'
namespace = 'google:nosave'
plugin_attrib = 'item'
plugin_multi_attrib = 'items'
interfaces = {'jid', 'source', 'value'}
def get_value(self):
return self._get_attr('value', '') == 'enabled'
def set_value(self, value):
self._set_attr('value', 'enabled' if value else 'disabled')
def get_jid(self):
return JID(self._get_attr('jid', ''))
def set_jid(self, value):
self._set_attr('jid', str(value))
def get_source(self):
return JID(self._get_attr('source', ''))
def set_source(self, value):
self._set_attr('source', str(value))
register_stanza_plugin(NoSaveQuery, Item)

View 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

View File

@@ -0,0 +1,63 @@
"""
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
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.settings import stanza
class GoogleSettings(BasePlugin):
"""
Google: Gmail Notifications
Also see <https://developers.google.com/talk/jep_extensions/usersettings>.
"""
name = 'google_settings'
description = 'Google: User Settings'
dependencies = set()
stanza = stanza
def plugin_init(self):
register_stanza_plugin(Iq, stanza.UserSettings)
self.xmpp.register_handler(
Callback('Google Settings',
StanzaPath('iq@type=set/google_settings'),
self._handle_settings_change))
def plugin_end(self):
self.xmpp.remove_handler('Google Settings')
def get(self, timeout=None, callback=None):
iq = self.xmpp.Iq()
iq['type'] = 'get'
iq.enable('google_settings')
return iq.send(timeout=timeout, callback=callback)
def update(self, settings, timeout=None, callback=None):
iq = self.xmpp.Iq()
iq['type'] = 'set'
iq.enable('google_settings')
for setting, value in settings.items():
iq['google_settings'][setting] = value
return iq.send(timeout=timeout, callback=callback)
def _handle_settings_change(self, iq):
reply = self.xmpp.Iq()
reply['type'] = 'result'
reply['id'] = iq['id']
reply['to'] = iq['from']
reply.send()
self.xmpp.event('google_settings_change', iq)

View 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')

View 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)

View 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)

View 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'}

View 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)

View 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)

View 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)

View File

@@ -14,9 +14,3 @@ from slixmpp.plugins.xep_0004.dataforms import XEP_0004
register_plugin(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

View File

@@ -23,7 +23,7 @@ class XEP_0004(BasePlugin):
name = 'xep_0004' name = 'xep_0004'
description = 'XEP-0004: Data Forms' description = 'XEP-0004: Data Forms'
dependencies = set(['xep_0030']) dependencies = {'xep_0030'}
stanza = stanza stanza = stanza
def plugin_init(self): def plugin_init(self):

View File

@@ -13,21 +13,22 @@ class FormField(ElementBase):
namespace = 'jabber:x:data' namespace = 'jabber:x:data'
name = 'field' name = 'field'
plugin_attrib = 'field' plugin_attrib = 'field'
interfaces = set(('answer', 'desc', 'required', 'value', plugin_multi_attrib = 'fields'
'options', 'label', 'type', 'var')) interfaces = {'answer', 'desc', 'required', 'value',
sub_interfaces = set(('desc',)) 'label', 'type', 'var'}
sub_interfaces = {'desc'}
plugin_tag_map = {} plugin_tag_map = {}
plugin_attrib_map = {} plugin_attrib_map = {}
field_types = set(('boolean', 'fixed', 'hidden', 'jid-multi', field_types = {'boolean', 'fixed', 'hidden', 'jid-multi',
'jid-single', 'list-multi', 'list-single', 'jid-single', 'list-multi', 'list-single',
'text-multi', 'text-private', 'text-single')) 'text-multi', 'text-private', 'text-single'}
true_values = set((True, '1', 'true')) true_values = {True, '1', 'true'}
option_types = set(('list-multi', 'list-single')) option_types = {'list-multi', 'list-single'}
multi_line_types = set(('hidden', 'text-multi')) multi_line_types = {'hidden', 'text-multi'}
multi_value_types = set(('hidden', 'jid-multi', multi_value_types = {'hidden', 'jid-multi',
'list-multi', 'text-multi')) 'list-multi', 'text-multi'}
def setup(self, xml=None): def setup(self, xml=None):
if ElementBase.setup(self, xml): if ElementBase.setup(self, xml):
@@ -163,8 +164,9 @@ class FieldOption(ElementBase):
namespace = 'jabber:x:data' namespace = 'jabber:x:data'
name = 'option' name = 'option'
plugin_attrib = 'option' plugin_attrib = 'option'
interfaces = set(('label', 'value')) interfaces = {'label', 'value'}
sub_interfaces = set(('value',)) sub_interfaces = {'value'}
plugin_multi_attrib = 'options'
FormField.addOption = FormField.add_option FormField.addOption = FormField.add_option

Some files were not shown because too many files have changed in this diff Show More