Compare commits

...

1120 Commits

Author SHA1 Message Date
Lance Stout
ee702f4071 Bump version to 1.1.5 2012-06-15 15:36:01 -07:00
Lance Stout
a08c2161a7 Ensure that ssl_invalid_cert returns PEM formatted certifcate data. 2012-06-15 15:29:53 -07:00
Lance Stout
0e36a01354 Bump version to 1.1.4 2012-06-13 09:17:08 -07:00
Lance Stout
c39ad7dfbb Prevent duplicate certificate expiration timers. 2012-06-13 09:13:33 -07:00
Lance Stout
b92ae706e9 Fix loading cached disco identity data. 2012-06-13 09:13:13 -07:00
Lance Stout
6997261c6b Bump version for 1.1.3 2012-06-09 11:32:03 -07:00
Lance Stout
6cfb5cb14c Add extra check for the cert in the expiration handler. 2012-06-09 11:01:45 -07:00
Lance Stout
8567d6034f Use False for use_tls for components.
A log message is shown for those who try to set it to True.

Fixes issue #171
2012-06-09 11:01:35 -07:00
Lance Stout
e06368f8cd Default use_tls to False for components.
Issue #171
2012-06-09 11:01:21 -07:00
Lance Stout
4b37a4706f Fix SSL handshake handling when not using legacy SSL.
Fixes issue #172
2012-06-09 11:01:11 -07:00
Lance Stout
7b1564947d Ensure that all SSL cert error handling is overridable using event handlers.
Relevant events:

    ssl_invalid_cert
    ssl_invalid_chain
    ssl_expired_cert
2012-06-09 11:00:55 -07:00
Lance Stout
f5652a667b Add 'presence' event, raised for all incoming presence stanzas. 2012-06-06 16:10:25 -07:00
Lance Stout
3b2c865a58 Bump version to 1.1.2 2012-06-06 12:26:15 -07:00
Lance Stout
db0e683d01 Don't request registration forms unless the register event is handled.
Some servers end the stream if registration can not be completed
in-band, which means always requesting the form can prevent regular
login.
2012-06-06 12:23:40 -07:00
Lance Stout
e29a9e0394 Bump version for 1.1.1 minor release. 2012-06-04 11:56:53 -07:00
Lance Stout
edf65f4f52 Include the default, unnamed group in self.client_roster.groups() 2012-06-04 11:54:25 -07:00
Lance Stout
98677fd602 Don't add cert expiration timer if no certs are being used. 2012-06-04 11:53:58 -07:00
Lance Stout
61a4f76c8d Update version and README for 1.1 2012-06-01 14:13:17 -07:00
Lance Stout
856a826eea Fix syntax error in line continuation. 2012-06-01 14:09:14 -07:00
Lance Stout
387ef513d6 Check that the session is still alive before sending data.
Fixes issue #168
2012-06-01 13:50:38 -07:00
Lance Stout
2858dbf57f Update development version number to prepare for 1.1 release. 2012-05-31 22:07:36 -07:00
Lance Stout
350a2b8bbc Preemptively mark threads as exited if calling disconnect(). 2012-05-31 22:04:45 -07:00
Lance Stout
c9093c9972 Handle not being able to connect using IPv6 if the host does not support it. 2012-05-27 16:33:21 -07:00
Lance Stout
d1ad31696e Fix X-FACEBOOK-PLATFORM mechanism to work with Python3. 2012-05-25 11:04:46 -07:00
Lance Stout
f49311ef9e Add better certificate handling.
Certificate host names are now matched (using DNS, SRV, XMPPAddr, and
Common Name), along with expiration check.

Scheduled event to reset the stream once the server's cert expires.

Handle invalid cert trust chains gracefully now.
2012-05-22 03:56:06 -07:00
Lance Stout
678e529efc Remove unused xmlstream test client.
It's in the repo history if we need it later.
2012-05-17 22:27:03 -07:00
Lance Stout
6ddb430fef Spell thirdparty correctly. 2012-05-16 12:00:00 -07:00
Lance Stout
74d1f88146 Prune unused conn_test code. 2012-05-16 11:57:55 -07:00
Lance Stout
7842c55da3 Add auth_success event.
The auth_success event is triggered upon successful SASL negotiation.
2012-05-15 14:26:25 -07:00
Lance Stout
f5beac2afa Use SASLPrepFailure as the exception name instead of UnicodeError. 2012-05-14 23:12:54 -07:00
Lance Stout
8a23f28dfa Add an exception handler for SASLprep failures. 2012-05-14 22:26:06 -07:00
Lance Stout
9c4886e746 Remove extra connection info so that examples run without modification.
GTalk users may still need to change the connect() call if dnspython is
not installed, as usual.
2012-05-14 22:17:39 -07:00
Lance Stout
e0bcd5d722 Add more documentation to the custom stanza examples. 2012-05-14 22:12:52 -07:00
Erick Pérez Castellanos
ba854e7d85 Added custom_stanza example 2012-05-14 21:47:43 -07:00
Lance Stout
4ded34ebc9 Add MUC events for room configuration changes.
New events:
    groupchat_config_status
    muc::[room JID]::config_status
2012-05-14 16:10:22 -07:00
Lance Stout
e918a86028 Make the error message better regarding hanged threads.
All event handlers which call disconnect() MUST be registered using
`add_event_handler(..., threaded=True)` in order to prevent temporarily
deadlocking until a timeout occurs.

This is required because disconnect() waits for the main threads to
exit before returning, including the event processing thread. Since
handlers registered without `threaded=True` run in the event processing
thread, the disconnect() call will deadlock.
2012-05-10 10:22:38 -07:00
Lance Stout
24234bf718 Update other examples to use threaded mode for handlers that call disconnect() 2012-05-06 20:19:02 -07:00
Lance Stout
ec99339140 Update send_client.py to call disconnect() from a threaded handler. 2012-05-06 20:07:05 -07:00
Lance Stout
03dedfc871 Windows doesn't support inet_pton. 2012-05-06 12:17:50 -07:00
Lance Stout
9e86a7b357 Tidy up and add tests for multi_attrib plugins. 2012-05-05 14:01:13 -07:00
Lance Stout
6a32417957 Merge pull request #163 from whooo/master
factory for recurring substanzas
2012-05-05 11:34:29 -07:00
Lance Stout
97a7be7dfa Fix loading plugins from custom modules when passing the module itself.
Loading plugins from custom modules when passed as a string still works.
2012-05-04 09:51:02 -07:00
Erik Larsson
fa86f956ef added multifactory and support for it to register_stanza_plugin 2012-04-30 22:19:17 +02:00
Lance Stout
a9acff5294 Collapse initial payload to a single stanza instead of a list if only one stanza is found. 2012-04-30 11:16:10 -07:00
Lance Stout
ad5b61de50 Add full support for initial payloads with adhoc commands, plus test. 2012-04-30 11:07:54 -07:00
Lance Stout
f53b815855 Allow providing initial payload to adhoc commands. 2012-04-30 08:27:10 -07:00
Lance Stout
bf8a9dc20d Add logging note about potential cause of disconnect() deadlock. 2012-04-29 14:48:14 -07:00
Lance Stout
08716c35fd Set a timeout when waiting for threads.
If calling disconnect() from a non-threaded event handler, deadlock can
happen as disconnect() is waiting for threads to close, but the event
runner is blocked by a handler waiting for disconnect() to return.

It is best to specify threaded=True for event handlers which may call
disconnect().
2012-04-29 14:45:00 -07:00
Lance Stout
fd81bab906 Use the correct 'from' jid when requesting vcards for avatars. 2012-04-29 13:33:53 -07:00
Lance Stout
1cf55c14b0 Don't raise errors when receiving an iq error for vcards. 2012-04-29 13:33:30 -07:00
Lance Stout
8b47159788 Populate the to attribute for message and presence stanzas if the server leaves it blank. 2012-04-26 15:46:18 -07:00
Lance Stout
2eeaf4d80c Use provided stanza ID. 2012-04-25 13:55:46 -07:00
Lance Stout
4d89d26a1c Prevent corrupting roster_update event with iq result. 2012-04-25 11:03:33 -07:00
Lance Stout
0cc14cee4d Ensure that SSL errors are handled in Py3.3 2012-04-24 16:11:49 -07:00
Lance Stout
a20a9c505d Track threads to ensure all have exited when disconnecting. 2012-04-22 18:13:36 -07:00
Lance Stout
913738444e Count and track the main threads, so we can delay disconnecting until all have quit. 2012-04-21 10:36:39 -07:00
Lance Stout
8ee30179ea Add _use_daemons flag to XMLStream to run all threads in daemon mode.
This WILL make the Python interpreter produce exceptions on shutdown.
2012-04-20 15:21:31 -07:00
Lance Stout
cb2469322b Handle using provided weakrefs as stanza parent references.
Fixes issue #159
2012-04-14 11:13:38 -04:00
Lance Stout
94aa6673ca Check for the stop event more aggressively in the send thread. 2012-04-13 08:27:11 -04:00
Lance Stout
4b2b2d16b8 Reset attempted SASL mech set after no suitable mechs are found. 2012-04-11 12:53:22 -04:00
Lance Stout
4cd5d3b3b5 Fix DNS resolution results for IP literals. 2012-04-10 14:08:33 -04:00
Lance Stout
e48e50c6ff Update setup.py with the latest plugins. 2012-04-09 21:45:19 -04:00
Lance Stout
01189376e2 Add initial support for XEP-0153. 2012-04-09 21:41:59 -04:00
Lance Stout
60195cf2dc Initial support for XEP-0231. 2012-04-08 23:27:19 -04:00
Lance Stout
15ef273141 Add a prefix to stanza ID values to ensure that they are unique per client. 2012-04-08 21:15:53 -04:00
Lance Stout
eed6da538a Undo the additional Iq result checks until further testing is done.
Revert "Check for Iq results based on both the sender's JID and the ID value."

This reverts commit 9ffde5ab37.
2012-04-08 16:30:52 -04:00
Lance Stout
d3e8993e22 Fix looking up local and cached vcards. 2012-04-08 16:01:47 -04:00
Lance Stout
8a8926c5e8 Fix errors in caps related to unwrapped disco data and full JIDs. 2012-04-08 16:00:36 -04:00
Lance Stout
f9d0ee824b Ensure that wrapped disco results retain requesting iq id. 2012-04-08 16:00:07 -04:00
Lance Stout
af099737ab Ensure that accessing self.api.settings works for plugins. 2012-04-08 15:59:47 -04:00
Lance Stout
9ffde5ab37 Check for Iq results based on both the sender's JID and the ID value. 2012-04-08 15:58:48 -04:00
Lance Stout
272ddf9f01 Add nickname element to the XEP-0054 plugin. 2012-04-07 21:16:36 -04:00
Lance Stout
259c84e99a Add initial XEP-0054 plugin. 2012-04-07 20:50:02 -04:00
Lance Stout
7391288668 Tidy up roster_received event and callbacks. 2012-04-07 17:30:25 -04:00
Lance Stout
7734aee7ad Prevent roster_update from firing twice after retrieving the roster. 2012-04-07 17:22:29 -04:00
Lance Stout
9f855b9679 Trigger got_online after resource information has been saved. 2012-04-07 16:23:24 -04:00
Lance Stout
aedbecd673 Correct the statemachine's ensure_any method.
It had not been updated to use the new condition instead of the old
threading event.
2012-04-06 17:39:51 -04:00
Lance Stout
83c5a4cd2f Pass JID objects to API callbacks and not strings. 2012-04-06 15:22:36 -04:00
Lance Stout
9c61c2882f Add support for XEP-0027 2012-04-06 15:22:23 -04:00
Lance Stout
e0dd9c3618 Simplify registering API handler defaults. 2012-04-06 15:09:26 -04:00
Lance Stout
4921c44d0a Don't break test plugins that use None instead of a stream object. 2012-04-06 15:09:26 -04:00
Lance Stout
3161f104c7 Update XEP-0012 plugin to use new api. 2012-04-06 15:09:26 -04:00
Lance Stout
898f5f4b51 Allow for registering a handler and setting it as default in one step. 2012-04-06 15:09:26 -04:00
Lance Stout
3ee3fdca91 Fix XEP-0115 with the new API registry. 2012-04-06 15:09:26 -04:00
Lance Stout
488f7ed886 Begin experiment with a centralized API callback registry.
The API registry generalizes the node handler system from the xep_0030
plugin so that other plugins can use it.
2012-04-06 15:09:25 -04:00
Lance Stout
51e5aee830 Add default mapping of localhost to ::1 and 127.0.0.1 2012-04-06 15:08:21 -04:00
Lance Stout
af13bea2b8 Fix MUC invite events so that they actually work. 2012-04-03 22:41:37 -07:00
Lance Stout
cdf0b353db Fix memory leak with adhoc command sessions.
Fixes issue #155
2012-04-03 11:02:55 -07:00
Lance Stout
48504ed5e2 Display IPv6 literal addresses in brackets. 2012-04-01 19:32:12 -07:00
Lance Stout
4d4d1e0ee5 Improve connection handling by not delaying until all DNS records are tried. 2012-03-30 10:12:44 -07:00
Lance Stout
c1d36cad46 Add better DNS resolver wrapper. 2012-03-30 10:12:43 -07:00
Lance Stout
aad2eb31fc Fix typo 2012-03-30 09:01:15 -07:00
Lance Stout
1bd7824f24 Tidy up the state machine and use a threading condition instead of an event.
Fixes issue #154
2012-03-28 23:58:38 -07:00
Lance Stout
912463ed6a Fix sending data after </stream>
Clearing the session_started_event before sending </stream> will
pause the send loop so that we don't continue sending data after
the </stream>.
2012-03-28 23:53:55 -07:00
Lance Stout
dda2473d35 Reset stream management state on session_end. 2012-03-27 23:27:24 -07:00
Lance Stout
94923ae898 Improve handling disconnections.
- Add option for disconnecting without sending </stream>:

    self.disconnect(send_close=False)

- Optionally distinguish between session_end and disconnected based
  on if </stream> was sent.

    self.end_session_on_disconnect = False
2012-03-27 23:24:42 -07:00
Lance Stout
f1fde07eb9 Add tests for bool_interfaces. 2012-03-27 21:16:53 -07:00
Lance Stout
a1ddd88208 Add support for a new type of stanza interface: bool
The set of bool_interfaces provides default behaviour for
checking/setting the presence of empty subelements.

The prime example of this would be:

    bool_interfaces = set(['required'])

This would mean that ``stanza['required']`` would return ``True`` for:

    <stanza>
       <required />
    </stanza>

and ``False`` for:

    <stanza />

Likewise, assigning ``stanza['required'] = True`` would add an empty
``<required />`` element, and setting it to ``False`` would remove
such an element if it exists.
2012-03-27 21:05:50 -07:00
Lance Stout
ee6a9b981a Simplify sending whitespace keepalives.
Now that we have the send lock, we can use now=True.
2012-03-27 20:53:27 -07:00
Lance Stout
9879c7af59 Make the XEP-0198 ack debug message less confusing. 2012-03-27 20:52:31 -07:00
Lance Stout
fa4c52e499 Correct handling of acks for XEP-0198 under heavy load. 2012-03-21 13:00:43 -07:00
Lance Stout
d5484808a7 Respect reattempt=False setting when reconnecting. 2012-03-21 10:28:26 -07:00
Lance Stout
1c83391948 Merge remote-tracking branch 'hansent/master' into develop 2012-03-20 11:50:57 -07:00
Lance Stout
59d1b8e131 Correct connect() documentation, don't delay attempts if reattempt=False.
See issue #152
2012-03-20 09:56:39 -07:00
Lance Stout
859822ff05 Fix unicode issues in test cases for Py3+ introduced by issue #150. 2012-03-19 14:24:45 -07:00
Lance Stout
3acc7d0914 Merge pull request #150 from correl/rpc_value_fixes
Updated XEP-0009 to handle unicode strings
2012-03-19 14:06:36 -07:00
Lance Stout
b077ef9150 Fix error in the registration example.
The now=True parameter was not being passed to allow the registration
submission to be submitted while the send queue is paused.
2012-03-19 06:05:15 -07:00
Lance Stout
e2ce5ae222 Add example for using user location.
Uses http://freegeoip.com to get an approximate location based
on the machine's IP address.
2012-03-18 23:42:03 -07:00
Lance Stout
73cabcb6ae Add initial support for XEP-0198 for stream management. 2012-03-18 01:02:19 -07:00
Lance Stout
fbdf2bed49 Add out_sync filter category.
Added option to XMLStream.send() to skip applying filters.

Filters in the out_sync group are synced with placing stanza content
either on the wire directly or into the send queue. Because of this,
out_sync filters should not block.
2012-03-18 00:59:45 -07:00
Lance Stout
33d01fb694 Fix requesting receipts on a message that has not been bound to a stream. 2012-03-16 23:42:55 -07:00
Lance Stout
ab2e43d052 Re-add support for special case of 'presence' expiry value. 2012-03-16 23:42:34 -07:00
Lance Stout
0c24fbdb06 Add pubsub examples.
Run pubsub commands via pubsub_client, and watch events as they come in
with pubsub_events.
2012-03-16 23:18:59 -07:00
Lance Stout
eb25998e72 Update subscription event expiry value to use time objects. 2012-03-16 23:16:17 -07:00
Lance Stout
eafd2aee93 Add events for configuration and subscription notifications.
New events:
    pubsub_config
    pubsub_subscription
2012-03-16 23:12:38 -07:00
Lance Stout
a6f3d740a2 Fix error when assigning form values that include booleans. 2012-03-16 22:02:21 -07:00
Lance Stout
19a6f61b44 Fix requiring receipt request messages to have ID values. 2012-03-16 22:01:56 -07:00
Lance Stout
58e0f1e6c3 Expand support for XEP-0184.
New stanza interfaces:

    Adding a message receipt request:

        msg['request_receipt'] = True

    Adding a message receipt:

        msg['receipt'] = '123-24234'

    Retrieving the acked message ID:

        ack_id = msg['receipt']
        print(ack_id)
        '123-24234'

New configuration options:

    auto_ack:
        If True, auto reply to messages that request receipts.

        Defaults to True

    auto_request:
        If True, auto add receipt requests to appropriate outgoing
        messages.

        Defaults to False
2012-03-16 10:51:25 -07:00
Lance Stout
96ff2d43c0 Explicitly set the desired SASL mech to ANONYMOUS if no username is provided. 2012-03-13 12:24:41 -07:00
Lance Stout
1b00b7e8df Correct handling SASL auth failures when forcing the use of a specific mechanism. 2012-03-13 11:07:14 -07:00
Lance Stout
7284ceb90c Move feature_rosterver to new system. 2012-03-12 20:04:11 -07:00
Lance Stout
24ec448b7f Move feature_starttls to new system. 2012-03-12 19:57:20 -07:00
Lance Stout
ed5a2f400d Move feature_session to new system. 2012-03-12 19:52:20 -07:00
Lance Stout
9596616b42 Move feature_mechanisms to new system. 2012-03-12 19:52:01 -07:00
Lance Stout
8d38fb511b Move feature_bind to new system. 2012-03-12 19:49:43 -07:00
Lance Stout
5a2cbbb731 Move XEP-0172 to new system. 2012-03-12 19:32:20 -07:00
Lance Stout
32d6f85649 Move XEP-0118 to the new system. 2012-03-12 19:32:20 -07:00
Lance Stout
a2b47e5749 Move XEP-0108 to new system. 2012-03-12 19:32:20 -07:00
Lance Stout
14d4062f4a Move XEP-0107 to new system. 2012-03-12 19:32:20 -07:00
Lance Stout
67972c5e84 Move XEP-0080 to the new system. 2012-03-12 19:32:20 -07:00
Lance Stout
3467ac18cc Move XEP-0163 to new system.
Also includes new register_pep() method for doing the necessary stanza
and disco registration, plus pubsub node event mapping.
2012-03-12 19:32:20 -07:00
Lance Stout
cabf27424f Cleanup plugin import logic.
Checking for a 'xep' or 'rfc' attribute is more reliable
for detecting an old style plugin than 'name'.
2012-03-12 19:32:20 -07:00
Lance Stout
162e955bd6 Enable using post_init() to resolve circular dependencies.
We really shouldn't have any. However, we may later introduce one
with XEP-0030 and XEP-0059.
2012-03-12 19:32:20 -07:00
Lance Stout
57d761b8a2 Move XEP-0115 to the new system. 2012-03-12 19:32:20 -07:00
Lance Stout
8b2023225c Remove extra logging statement, add backward compatible references. 2012-03-12 19:32:20 -07:00
Lance Stout
f8f2b541db Handle loading plugins on demand.
Plugins that are referenced as dependencies, but have not been
registered now will be imported. Newer plugins should register
themselves automatically, but older style plugins will be
explicitly registered after import.
2012-03-12 19:32:20 -07:00
Lance Stout
9d645ad5cd Update the list of all stream feature plugins. 2012-03-12 19:32:20 -07:00
Lance Stout
610d366bdb Ensure the adhoc command items node exists.
If the plugin is loaded and no commands are defined, we can at least
return a proper empty response instead of an item-not-found error.
2012-03-12 19:32:20 -07:00
Lance Stout
64c46562d3 Move XEP-0249 to the new system. 2012-03-12 19:32:20 -07:00
Lance Stout
87d6ade06d Move XEP-0224 to new system. 2012-03-12 19:32:20 -07:00
Lance Stout
4a009515c1 Move XEP-0203 to new system. 2012-03-12 19:32:20 -07:00
Lance Stout
6497857495 Move XEP-0202 to new system. 2012-03-12 19:32:20 -07:00
Lance Stout
5a324c01de Move XEP-0199 to new system. 2012-03-12 19:32:19 -07:00
Lance Stout
17279de4a3 Move XEP-0184 to new system. 2012-03-12 19:32:19 -07:00
Lance Stout
34a7a62c35 Move XEP-0128 to new system. 2012-03-12 19:32:19 -07:00
Lance Stout
2305cc61fd Move XEP-0092 to new system. 2012-03-12 19:32:19 -07:00
Lance Stout
2f677c98f8 Move XEP-0086 to new system. 2012-03-12 19:32:19 -07:00
Lance Stout
3fda053606 Move XEP-0085 to the new system.
Optimized handlers so that only one is needed.
2012-03-12 19:32:19 -07:00
Lance Stout
6d855ec06c Move XEP-0082 to new system. 2012-03-12 19:32:19 -07:00
Lance Stout
23cc62fe7c Move XEP-0078 to new system. 2012-03-12 19:32:19 -07:00
Lance Stout
26ea67d211 Move XEP-0045 to new system.
Still needs updating to the new format.
2012-03-12 19:32:19 -07:00
Lance Stout
d43cd9fa54 Move XEP-0033 to new system.
Still needs updating to the new format.
2012-03-12 19:32:19 -07:00
Lance Stout
6f337b5425 Move XEP-0012 to new system.
Still needs to update to the current plugin format though.
2012-03-12 19:32:19 -07:00
Lance Stout
d104a5fe75 Move XEP-0009 to new system. 2012-03-12 19:32:19 -07:00
Lance Stout
cdd69c6842 Move XEP-0077 to the new system. 2012-03-12 19:32:19 -07:00
Lance Stout
4a3a9067d4 Move XEP-0066 to new system. 2012-03-12 19:32:19 -07:00
Lance Stout
1aecb2293a Move XEP-0060 to new system. 2012-03-12 19:32:19 -07:00
Lance Stout
ad8fd91b7a Move XEP-0050 to new system. 2012-03-12 19:32:19 -07:00
Lance Stout
1f5a3a4445 Move XEP-0047 to new system. 2012-03-12 19:32:19 -07:00
Lance Stout
be363e0b46 Move XEP-0004 to new system. 2012-03-12 19:32:19 -07:00
Lance Stout
a104cd6dae Tidy up disco plugin. 2012-03-12 19:32:19 -07:00
Lance Stout
e287282782 Moving backwards compatibility shims to __init__ files. 2012-03-12 19:32:07 -07:00
Lance Stout
8b06d10415 Update XEP-0030 and XEP-0059 to new system. 2012-03-12 16:24:18 -07:00
Lance Stout
1a153487c3 Add tests for new plugin manager. 2012-03-12 16:24:18 -07:00
Lance Stout
01b2499915 Introduce new plugin system.
The new system is backward compatible and will load older style plugins.

The new plugin framework allows plugins to track their dependencies, and
will auto-enable plugins as needed.

Dependencies are tracked via a class-level set named `dependencies` in
each plugin.

Plugin names are no longer tightly coupled with the plugin class name,
Pso EP8 style class names may be used.

Disabling plugins is now allowed, but ensuring proper cleanup is left to
the plugin implementation.

The use of a `post_init()` method is no longer needed for new style
plugins, but plugins following the old style will still require a
`post_init()` method.
2012-03-12 16:24:18 -07:00
Lance Stout
9f43d31bf5 Add setting for maximum number of reconnection attempts.
Setting self.reconnect_max_attempts to a non-None value will limit
the number of times a connection attempt will be made before quiting
and raising a 'connection_failed' event.
2012-03-12 16:19:18 -07:00
Lance Stout
a318beded4 Update plugin list and use correct names. 2012-03-11 16:34:41 -07:00
Lance Stout
5f4b528e6b Ensure that result stanzas are returned, as expected. 2012-03-11 16:13:19 -07:00
Lance Stout
f759b0ada1 Add support for XEP-0108: User Activity. 2012-03-11 12:37:54 -07:00
Lance Stout
7d89fa27a8 Expand support of XEP-0172 (user nickname) to include PEP. 2012-03-11 00:22:28 -08:00
Lance Stout
10ec92f7c6 Add support for XEP-0107, User Mood. 2012-03-10 23:32:20 -08:00
Lance Stout
58d2f317a0 Fix plugin loading logs for XEP-0118 and XEP-0163. 2012-03-10 23:31:54 -08:00
Lance Stout
34b094561f Add support for XEP-0080. 2012-03-10 12:54:31 -08:00
Lance Stout
91155444c0 Resolve plugin dependency chains with XEP-0115.
The post_init() system can only reliably handle a single layer
of dependencies between plugins, but PEP plugins with XEP-0115
exceed that limit and plugins can be post_init'ed out of order. To
resolve this, we will special case XEP-0115 to be post_init'ed
first until the new plugin system with dependency tracking is
stable.
2012-03-10 12:48:35 -08:00
Lance Stout
7f71ac7e0a Add user tune feature to disco, not just notifications. 2012-03-10 10:54:24 -08:00
Lance Stout
e5fc59a4c6 Ensure post init works for XEP-0118. 2012-03-10 10:44:53 -08:00
Lance Stout
549a9ab472 Add support for XEP-0118.
See examples/user_tune.py for a demonstration using the currently
playing song in iTunes.
2012-03-10 10:30:32 -08:00
Lance Stout
09720dcf42 Fix XEP-0163's updating of caps. 2012-03-10 10:20:06 -08:00
Lance Stout
ec044affd4 Only auto-broadcast caps changes after a session has started. 2012-03-10 10:19:43 -08:00
Lance Stout
af39945009 Add XEP-0163 plugin.
This is just a very simple wrapper for XEP-0030, XEP-0115, and XEP-0060
for adding interests to caps information, and publishing.
2012-03-10 09:23:47 -08:00
Lance Stout
78a50d0237 Add support for pubsub notification events.
Publishes, retractions, purges, and deletions now raise the events:

- pubsub_publish
- pubsub_retract
- pubsub_purge
- pubsub_delete

In addition, custom events may be raised based on the node that
generated the notification. For example:

xmpp['xep_0060'].map_node_event('http://jabber.org/protocol/tune',
                                'user_tune')

will allow for using the events:

- user_tune_publish
- user_tune_retract
- user_tune_purge
- user_tune_delete
2012-03-10 00:07:56 -08:00
Lance Stout
861d279b08 Correct missing pubsub#event stanzas and interfaces. 2012-03-10 00:07:15 -08:00
Lance Stout
eb1a32fc90 Fix setup.py to include the rosterver stream feature plugin. 2012-03-08 16:21:22 -08:00
Lance Stout
4610a6615c Add tests for roster versioning. 2012-03-07 16:11:59 -08:00
Lance Stout
4cb8a8d389 Modify the cert event to provide the PEM encoded cert in all cases. 2012-03-07 15:03:35 -08:00
Lance Stout
a71823dc04 Add support for roster versioning.
This was XEP-0237, but is now part of RFC 6121.

Roster backends should now expose two additional methods:

version(jid):
    Return the version of the given JID's roster.
set_version(jid, version):
    Update the version of the given JID's roster.

A new state field will be passed to the backend if an item
has been marked for removal. This is 'removed' which will
be set to True.
2012-03-07 14:55:27 -08:00
Lance Stout
d41ada6b66 Cleanup logging when loading a custom plugin. 2012-03-05 11:30:36 -08:00
Lance Stout
fdfe2cd64f Propagate save option when setting a roster backend. 2012-03-05 11:28:10 -08:00
Lance Stout
7b51c6f5cc Save existing roster content when setting a new backend. 2012-03-05 11:12:13 -08:00
Lance Stout
be7f07ad12 Prevent excess loading from the roster db.
Fixes issue #148
2012-03-05 11:11:35 -08:00
Lance Stout
830db11b41 Ensure that roster nodes aren't empty strings.
This would happen when receiving presence without a 'to' field, which
happens when receiving presence from other resources for the same account.
2012-03-05 11:08:57 -08:00
Lance Stout
53bcd33e1d Let disconnect() wait for its lock for a few seconds.
This should eliminate most debug statements about not being
able to acquire a lock during disconnect.
2012-02-22 07:57:13 -08:00
Lance Stout
e3d596c9fa Update XEP-0085 plugin to work with both ElementTree and cElementTree
Each state element must have its own stanza class now. A stanza class
with an empty name field causes errors in ElementTree, even though
it works fine with cElementTree.
2012-02-19 20:28:31 -08:00
Lance Stout
ecd6ad6930 Fix incompatibility with clearing an element between ElementTree and cElementTree 2012-02-19 20:27:53 -08:00
Lance Stout
c36073b40e xml.etree.ElementTree raises ExpatError instead of SyntaxError or ParseError. 2012-02-19 20:27:19 -08:00
Lance Stout
afe0d16797 Centralize references to ET to make switching implementations easier. 2012-02-19 20:26:40 -08:00
Lance Stout
977fcc0632 Fix instances of using undefined variables. 2012-02-18 11:56:10 -08:00
Lance Stout
94b57d232d More pyflakes cleanup. 2012-02-18 11:44:05 -08:00
Lance Stout
7cdedb2ec0 More import cleanup based on pyflakes. 2012-02-18 11:40:34 -08:00
Lance Stout
676324805e Use JID objects when dealing with roster items. 2012-02-18 11:39:47 -08:00
Lance Stout
7d74a7b027 More extraneous import cleanup. 2012-02-17 14:59:56 -08:00
Lance Stout
9d5eb864d1 More import cleanups based on pyflakes results. 2012-02-17 14:41:31 -08:00
Lance Stout
86a482e032 Fix pyflakes complaints for XEP-0115 plugin. 2012-02-17 11:40:51 -08:00
Lance Stout
c43c7be86c Make last_xml usage a little more explict. 2012-02-17 11:40:07 -08:00
Lance Stout
c58462f154 Fix undeclared variable usage for reconnect. 2012-02-17 11:12:48 -08:00
Correl Roush
31d3e3b2b6 Updated XEP-0009 to handle unicode strings 2012-02-17 12:24:44 -05:00
Lance Stout
fb2582e53b Fix fixing remove_stanza()
Fixes issue #146
2012-02-16 07:25:44 -08:00
Lance Stout
d807613117 Don't retrieve cert until a connection is made. 2012-02-16 07:02:56 -08:00
Lance Stout
6d922d00c3 Fix remove_stanza().
Fixes issue #146
2012-02-16 07:02:19 -08:00
Lance Stout
61ea84093b Don't shutdown completely after handling SyntaxError.
The ``shutdown = True`` line was preventing the stream from reconnecting
after handling the error.

Fixes issues #101, #145
2012-02-10 19:28:12 -08:00
Lance Stout
e76d6a481f Fix undefined variable references when DNS timeouts. 2012-02-10 19:20:17 -08:00
Lance Stout
c1357717d9 Use '=' as base64 value for empty string SASL results. 2012-02-09 22:01:11 -08:00
Lance Stout
ca5145c210 Fix IPv6 query logging. 2012-02-10 06:46:51 +01:00
Lance Stout
1a272fd276 Add support for querying and connecting to IPv6 addresses.
Tested using servers provided by Florian Jensen (flosoft.biz)
during the 2012 FOSDEM XMPP Summit.

Fixes issue #94.
2012-02-09 21:28:28 -08:00
Lance Stout
952260b423 Add ssl_cert event (direct).
The payload is a dictionary of parsed cert data, as provided by
Python's getpeercert() socket method. It unfortunately does not
provide much detail beyond basic info.
2012-02-04 14:16:37 +01:00
Lance Stout
caa967105c Add more XEP-0047 tests. 2012-02-03 16:29:38 +01:00
Lance Stout
d565e4be20 Fix XEP-0184 imports 2012-02-03 16:08:27 +01:00
Lance Stout
85dd005abc Fix infinite callback loop. 2012-02-03 16:03:46 +01:00
Lance Stout
021c57205f Don't assume data is ASCII in saslprep. 2012-02-03 16:01:54 +01:00
Lance Stout
261a501afc Merge remote-tracking branch 'whooo/master' into develop 2012-02-03 15:23:01 +01:00
Erik Larsson
9a38a101d2 Added fritzy to the copyright for xep_0184 2012-02-03 15:17:01 +01:00
Lance Stout
4665c5cf1a Fix data stanza based on test results. 2012-02-02 19:19:50 +01:00
Lance Stout
bd52a5e6c1 Initial, mostly working XEP-0047 plugin.
This is inspired by the version from macdiesel and tomstrummer, but
their version was heavily linked with XEP-0096 and focused solely
on file transfer. This version is a more generic implementation.
2012-02-02 18:27:23 +01:00
Lance Stout
f98e5a03de Fix typo s/is_set/is_set() 2012-02-02 18:14:48 +01:00
Erik Larsson
2217c69757 Added plugin for XEP-0184 2012-02-02 14:29:27 +01:00
Thomas Hansen
5a4df56836 examples: fix rpc examples. __init__ method was wrongly named "__init" causing proxy and handler class to not be initialized. 2012-01-31 12:38:41 -06:00
Lance Stout
3ab7c8bcc3 Make socket_error run as a direct event to ensure that it is handled.
Socket errors that occur before stream processing begins could not be
handled as the event loop would not be running yet.

Resolves issue #142
2012-01-28 18:54:46 -08:00
Lance Stout
8f25acd0f3 Bump version number in develop branch to 1.0.1dev. 2012-01-25 20:44:41 -08:00
Lance Stout
999f1932cc Merge pull request #138 from rhcarvalho/patch-2
Set default argument value.
2012-01-25 20:41:45 -08:00
Lance Stout
69940a8ab9 Fix a few typos. 2012-01-24 00:07:31 -08:00
Lance Stout
13158e3cdf Revert the X-GOOGLE-TOKEN mech to not perform HTTP requests.
Added new example for how to retrieve a Google token, following
the best case, non-browser, workflow. Other thirdparty auth
mechs (Facebook, MSN) follow a similar pattern of using an
access token.
2012-01-23 23:58:40 -08:00
Lance Stout
f06589c913 Merge pull request #140 from rhcarvalho/patch-3
Fix ValueError when line has more than one '='.
2012-01-23 10:53:19 -08:00
Rodolfo Carvalho
2735b680b9 Fix ValueError when line has more than one '='. 2012-01-22 18:32:32 -02:00
Rodolfo Henrique Carvalho
5f1d4ce433 Set default argument value.
Without this features/feature_mechanisms/mechanisms.py throws an error when calling the method `process' without arguments on this mechanism.
2012-01-22 01:53:07 -02:00
Lance Stout
25f87607aa Add support for X-GOOGLE-TOKEN.
This is mainly just useful for authenticating without using TLS.

If an access token is not provided, an attempt will be made to
retrieve one from Google.
2012-01-21 00:44:03 -08:00
Lance Stout
f81fb6af44 Require explicitly setting access_token value.
Silently substituting the password field was nice, but for mechs
that can use either the password or an access token, it makes
things very difficult. This really only affects MSN clients since
Facebook clients should already be setting the api key.
2012-01-21 00:19:59 -08:00
Lance Stout
bb0a5186d6 Handle SASLCancelled and SASLError exceptions. 2012-01-21 00:19:08 -08:00
Lance Stout
baad907422 Add missing SASL <abort /> stanza 2012-01-21 00:17:49 -08:00
Lance Stout
1022fc0060 Make things work with Python3's byte semantics. 2012-01-20 02:27:30 -08:00
Lance Stout
3a22d798f8 Allow attempting multiple SASL mechs during a single stream.
Instead of disconnecting when the first chosen mech fails, we will
try all of them once.
2012-01-20 02:01:08 -08:00
Lance Stout
71ea430c62 Add support for X-FACEBOOK-PLATFORM SASL mechanism.
This requires an extra credential for SASL authentication:

xmpp = ClientXMPP('user@chat.facebook.com', '...access_token...')
xmpp.credentials['api_key'] = '...api_key...'
2012-01-20 01:24:05 -08:00
Lance Stout
0d2125e737 Add an extra config dict to store SASL credentials.
We'll need extra things beyond just a password, such as api_key.
2012-01-20 01:08:25 -08:00
Lance Stout
02f4006153 Add basic start for a client side XEP-0077 plugin. 2012-01-19 02:37:36 -08:00
Lance Stout
b25668b5b7 Fix detecting end of result set paging. 2012-01-18 19:57:49 -08:00
Lance Stout
bb3080e829 Merge branch 'docs' into develop
Conflicts:
	docs/index.rst
2012-01-18 15:36:18 -08:00
Lance Stout
bd85e95398 Gah, too many branch conflicts. 2012-01-18 15:34:49 -08:00
Lance Stout
22cc194ed8 Merge branch 'docs' of github.com:fritzy/SleekXMPP into docs
Conflicts:
	docs/index.rst
2012-01-18 15:34:12 -08:00
Lance Stout
79b71228c1 Fix some more merge conflicts. 2012-01-18 15:31:45 -08:00
Lance Stout
fd515d807c Add example of accessing plugins to the README. 2012-01-18 15:22:19 -08:00
Lance Stout
4f4c121d9b Fix merge errors and bot example. 2012-01-18 15:16:56 -08:00
Lance Stout
72e1ab47fc Merge branch 'docs' into develop
Conflicts:
	docs/_static/haiku.css
	docs/_static/header.png
	docs/conf.py
	docs/getting_started/muc.rst
	docs/index.rst
	examples/muc.py
2012-01-18 15:04:33 -08:00
Lance Stout
3575084640 Update home page to include bot example, and example of using a plugin. 2012-01-18 14:24:23 -08:00
Lance Stout
1e01903072 Revert "Remove stream feature handlers on session_start."
This reverts commit 4274f49ada.

The SASL mech was choking on this, so let's send it back for some
more refining.
2012-01-18 11:51:00 -08:00
Lance Stout
3672856ab4 Fix roster key issue for non-JID keys. 2012-01-17 23:10:14 -08:00
Lance Stout
86d8736dcc Hash JIDs based on full JID string.
This makes JID objects equivalent to strings in dictionaries, etc.

>>> j = JID('foo@example.com')
>>> s = 'foo@example.com'
>>> d = {j: 'yay'}
>>> d[j]
'yay'
>>> d[s]
'yay'
>>> d[s] = 'yay!!'
>>> d[j]
'yay!!'
2012-01-17 23:03:48 -08:00
Lance Stout
2923f56561 Pre-parse StanzaPath paths to speed up matching.
The parsing and namespace cleaning isn't terribly expensive, but it does
add up. It was adding an extra 5sec when processing 100,000 basic
message stanzas.
2012-01-17 22:28:44 -08:00
Lance Stout
4274f49ada Remove stream feature handlers on session_start.
Based on profiling, using around 35 stream handlers quarters the number
of basic message stanzas that can be processed in a second, in
comparison to only using the bare minimum of four handlers.

To help, we can drop handlers for stream features once the session
has started. So that we can re-enable these handlers when a stream
must restart, the 'stream_start' event has been added which fires
whenever a stream header is received.

The 'stream_start' event is a more generic replacement for the
existing start_stream_handler() method.
2012-01-17 22:14:24 -08:00
Lance Stout
a4b27ff031 Merge pull request #137 from rhcarvalho/patch-1
Use jid.bare as a key instead of a JID instance.
2012-01-16 11:44:42 -08:00
Rodolfo Henrique Carvalho
f49b6fa79f Use jid.bare as a key instead of a JID instance. 2012-01-16 16:59:45 -02:00
Lance Stout
7b854a190e Tidy up and update the plugin __init__ file. 2012-01-15 22:51:59 -08:00
Lance Stout
947d1ffbb3 Fix xep_0030 reference warning. 2012-01-14 17:12:39 -08:00
Lance Stout
de35848500 Don't serialize XML unless we need to. 2012-01-14 10:54:48 -08:00
Lance Stout
1ae219025a Don't dump exception logs for XML stream parsing errors.
The exceptions are handled, so we don't need to clutter the output logs.
2012-01-12 22:26:15 -08:00
Lance Stout
e8b2dd6698 Update Roster stanza to use RosterItem substanzas.
get_roster() now returns the Iq result stanza instead of True (stanzas
also evaluate to True).
2012-01-12 17:21:43 -08:00
Lance Stout
c0074f95b1 update_caps() can now do presence broadcasting.
As part of adding this feature:

    - fixed bug in update_caps() not assigning verstrings
    - fixed xep_0004 typo
    - can now use None as a roster key which will map to boundjid.bare
    - fixed using JID objects in disco node handlers
    - fixed failing test related to get_roster

Several of these bugs I've fixed before, so I either didn't push them
earlier, or I clobbered something when merging. *shrug*
2012-01-11 16:39:55 -08:00
Lance Stout
a79ce1c35e Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2012-01-10 20:05:25 -08:00
Lance Stout
1eb69f7075 Make the roster easier to inspect.
The __repr__ version now looks like a regular dictionary.
2012-01-10 20:03:22 -08:00
Lance Stout
a86935a42f Make get_roster(block=False) work properly.
Fixes issue #136
2012-01-10 19:57:38 -08:00
Lance Stout
1674bd753e Fix setup.py Unicode issue with README.rst
Fixes issue #135
2012-01-09 15:38:19 -08:00
Lance Stout
6b9a55e62d Sync with Suelta. 2012-01-07 00:19:08 -05:00
Lance Stout
c578ddeb1a Add support for MSN with X-MESSENGER-OAUTH2 SASL support.
NOTE: This requires already having the access token. It does NOT
perform any OAuth requests.
2012-01-06 23:31:58 -05:00
Lance Stout
8ef7188dae Fix client_roster when the bare JID changes after binding.
Adds session_bind event.
2012-01-06 23:30:14 -05:00
Lance Stout
738ec92b8e Fix a few typos. 2012-01-05 13:11:42 -05:00
Lance Stout
be9e26b4a3 Apply Te-Je's MUC guide patch. 2012-01-05 13:09:20 -05:00
Lance Stout
b345c227b2 More &yet branding 2012-01-05 13:07:44 -05:00
Lance Stout
c7e95c8dec Add &yet contact info 2012-01-05 12:28:30 -05:00
Lance Stout
3a4e3d3f51 Update doc settings to new theme, add examples, use 1.0 2012-01-05 12:13:06 -05:00
Lance Stout
8fd2efa2fa Merge branch 'develop-1.1' into develop 2012-01-05 11:33:47 -05:00
Lance Stout
97378998a5 Break the docs out into their own branch. 2012-01-05 11:31:54 -05:00
Lance Stout
79f1aa0e1b Update version and README for 1.0 release. 2012-01-03 17:04:15 -05:00
Lance Stout
fb5a6a7d71 Merge pull request #132 from rhcarvalho/master
Fix a typo in several files.
2012-01-02 13:10:46 -08:00
Rodolfo Carvalho
7d1c5f4a2b Fix a typo in several files.
This fixes several instances of "intial" for "initial".
2012-01-02 18:59:39 -02:00
Lance Stout
6b6995bb0b Merge branch 'develop' into develop-1.1 2011-12-31 21:17:01 -05:00
Lance Stout
27c658922e Fix handing caps in Python3, allow update_caps() call before process() 2011-12-31 21:15:40 -05:00
Lance Stout
1b0fd76b45 Merge pull request #131 from rhcarvalho/master
Small changes to the examples
2011-12-31 17:27:57 -08:00
Lance Stout
35954cdc90 Fix a few holes in caps.
Protip: Don't test using a custom disco handler that always returns the
same feature set :p
2011-12-31 19:18:00 -05:00
Rodolfo Carvalho
46e93bea09 Remove unused import.
I forgot about this one before!
2011-12-31 20:14:24 -02:00
Rodolfo Carvalho
cbc6a0296b Ask interactively for missing command line arguments.
Instead of complaining that the arguments were not given, ask interactively for input.
This example was the only one to behave differently from the others.
2011-12-31 19:54:14 -02:00
Rodolfo Carvalho
cc63bef179 Remove unused imports in the examples. 2011-12-31 19:50:53 -02:00
Rodolfo Carvalho
cbcfa156c4 Add missing import. 2011-12-31 19:48:03 -02:00
Lance Stout
fa912aeb84 Merge branch 'develop' into develop-1.1 2011-12-31 12:34:17 -05:00
Lance Stout
4a12e1059a Add proxy docs. 2011-12-31 12:33:32 -05:00
Lance Stout
9a5e2ae768 Merge branch 'develop' into develop-1.1 2011-12-31 02:16:08 -05:00
Lance Stout
f9cd051209 Add echo component briefing. 2011-12-31 02:00:54 -05:00
Lance Stout
e0545bf0bc Merge branch 'develop' into develop-1.1 2011-12-31 01:29:12 -05:00
Lance Stout
03bc38f7e3 Add docs on using Iq stanzas. 2011-12-31 01:28:41 -05:00
Lance Stout
4e23a4e08e Merge pull request #130 from rhcarvalho/master
Some small fixes
2011-12-30 20:14:54 -08:00
Lance Stout
d817d64c65 Enable caps stream feature. 2011-12-30 22:34:57 -05:00
Lance Stout
8a29ec67ac Add XEP-0115 plugin.
Finally
2011-12-30 21:45:25 -05:00
Lance Stout
6722b0224a Add option to disable condensing and converting form values.
XEP-0115 needs to use the raw XML character data.
2011-12-30 21:43:39 -05:00
Lance Stout
8eb225bdec Add option for disabling identity and feature deduplication.
XEP-0115 requires detecting duplicates, so we can't always silently
ignore them.
2011-12-30 20:53:18 -05:00
Lance Stout
a7df76a275 Add 'supports' and 'has_identity' node handlers. 2011-12-30 20:52:44 -05:00
Lance Stout
efae8f3369 Automatically use local disco based on the JID. 2011-12-30 20:51:41 -05:00
Lance Stout
a11e6c0b77 Be more lenient on required arguments to disco node handlers. 2011-12-30 20:51:02 -05:00
Lance Stout
1bb0b38868 Make the disco logs nicer. 2011-12-30 20:50:15 -05:00
Rodolfo Carvalho
8cafa8578f Update examples to use the block'' argument instead of the deprecated threaded''. 2011-12-30 17:25:03 -02:00
Rodolfo Carvalho
b74ea47650 Fix docstring of a method of Message stanzas. 2011-12-30 17:08:32 -02:00
Rodolfo Carvalho
2dc230a68b Replace pydns with dnspython in the comments of the examples. 2011-12-30 00:08:05 -02:00
Lance Stout
4df1641689 Add set_info disco handler. 2011-12-28 11:46:13 -05:00
Lance Stout
5ef0b96d5c Fix caching for clients. 2011-12-28 11:37:05 -05:00
Lance Stout
d979b5f2b9 Add caching support to xep_0030.
New plugin configuration options:

    use_cache    - Enable caching disco info results. Defaults to True
    wrap_results - Always return disco results in an Iq stanza. Defaults
                   to False

Node handler changes:

    Handlers now take four arguments: jid, node, ifrom, data

    Most older style handlers will still work, depending on if they
    raise a TypeError for incorrect number of arguments. Handlers that
    used *args may not work.

New get_info options:

    cached - Passing cached=True to get_info() will attempt to load
             results from the cache. If nothing is found, a query
             will be sent as normal. If set to False, the cache
             will be skipped, even if it contains results.

New method:

    supports() - Given a JID/node pair and a feature, return True
                 if the feature is supported, False if not, and
                 None if there was a timeout. By default, the search
                 will use the cache.
2011-12-28 10:16:31 -05:00
Lance Stout
1a61bdb302 Ensure that stanza plugins work as expected if the XML is appended. 2011-12-28 09:53:22 -05:00
Lance Stout
e8545dd2bc Merge branch 'develop-1.1' of github.com:fritzy/SleekXMPP into develop-1.1 2011-12-27 18:05:55 -05:00
Lance Stout
2f2ebb37e4 Merge branch 'develop' into develop-1.1 2011-12-27 18:05:42 -05:00
Lance Stout
522f0dac16 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-12-27 18:03:08 -05:00
Lance Stout
cd5ae944ec Merge pull request #128 from correl/rpc_value_conversion_fix
XEP-0009 tests updated for Python3
2011-12-27 15:03:01 -08:00
Lance Stout
42a86fe0d4 Disconnect when a SyntaxError is found.
This should resolve issue #102
2011-12-27 18:01:26 -05:00
Correl Roush
e928b9c434 XEP-0009: Updated tests to work in python 3 2011-12-20 21:19:51 -05:00
Lance Stout
fb55d9e9d1 Add comma to fix pubsub error conditions.
Fixes issue #127
2011-12-20 12:30:35 -05:00
Lance Stout
74e7e5a291 Merge pull request #125 from correl/rpc_value_conversion_fix
XEP-0009 XML-RPC value conversion fixes and unit tests
2011-12-20 09:29:03 -08:00
Correl Roush
6c58b8cc4b XEP-0009: Updated RPC value conversion code
Updated the XML-RPC value conversion to correctly apply namespaces, and
fixed an error uncovered by the tests in the XML -> Python conversion of
dateTime values.
2011-12-20 02:03:06 -05:00
Correl Roush
2b3d11a7a5 XEP-0009: Added value conversion unit tests
Added tests for bidirectional conversion of all XML-RPC data types
2011-12-20 02:01:34 -05:00
Correl Roush
9950208d06 Fixes Issue #123: Corrected boolean xml to python conversion 2011-12-16 17:41:16 +00:00
Lance Stout
a67e16d1b7 Merge pull request #122 from correl/acl_check_fix
XEP-0009: ACL.check fix
2011-12-15 16:14:29 -08:00
Correl Roush
c98a22e065 Fixed Issue 93: ACL.check jid parameter should be a string value 2011-12-15 21:58:33 +00:00
Lance Stout
8f9d1bcfe0 Merge branch 'develop-1.1' of github.com:fritzy/SleekXMPP into develop-1.1 2011-12-15 12:04:14 -08:00
Lance Stout
a7a2fd1d5b Merge branch 'develop' into develop-1.1 2011-12-15 12:03:52 -08:00
Lance Stout
d496417deb Allow XEP-0082 to return datetime objects without having to format and reparse. 2011-12-15 12:02:08 -08:00
Lance Stout
f6e30edbc4 Log received data AFTER filtering.
This allows applications to filter out sensitive information, such
as passwords, so that it won't appear in the logs.

It does mean that the debug logs will not show the actual received
data, and there will be no indication of tampering, unless the
filter author explicitly logs and notes that a change was made.
2011-12-14 21:14:27 -08:00
Lance Stout
45ed68006f Add tests for filters. 2011-12-13 20:05:31 -08:00
Lance Stout
dcb0d8b00e Merge branch 'develop' into develop-1.1 2011-12-13 09:38:27 -08:00
Lance Stout
116bb6e1b9 Use OrderedDicts instead of regular dictionaries when returning values from forms. 2011-12-13 09:00:45 -08:00
Lance Stout
9c6dde5d22 Ensure that item fields have the proper type.
The item fields were not setting their type based on the reported
field's type attribute, so values were not being encoded properly.

Fixes issue #121
2011-12-13 08:59:39 -08:00
Lance Stout
cb635dcd5a Add parameter docs for add_filter. 2011-12-12 22:37:19 -08:00
Lance Stout
eff3330e75 Add support for incoming/outgoing filters.
A filter accepts and returns a stanza, but potentially modified.

To prevent sending/receiving a stanza, a filter may return None.

Incoming:
    self.add_filter('in', in_filter)

Outgoing:
    self.add_filter('out', out_filter)

Filters are applied in the order thay are added. However, you may
add an order parameter, which is the place in the list to insert the
filter:

    self.add_filter('in', in_filter, order=0)
2011-12-12 22:17:07 -08:00
Lance Stout
fc8a13df5a Allow disco info/items handlers to return full Iq stanzas.
Only allowing handlers to return a DiscoInfo/DiscoItem stanza works
for the majority of cases, but does not allow for the addition of
an RSM stanza, or other extensions.

An Iq stanza returned by a handler must already be configured as
a reply.
2011-12-12 19:38:32 -08:00
Lance Stout
85e9042db6 Pass the Iq stanza to disco item handlers. 2011-12-12 16:34:24 -08:00
Lance Stout
62e6d6fb4c Fix iterable substanzas when added as normal plugin.
If an iterable plugin was an enabled, it wasn't added to
the iterables list.
2011-12-11 17:04:58 -08:00
Lance Stout
16c72e8efd Use UTC for xep_0082.date. 2011-12-09 23:59:33 -08:00
Lance Stout
efe1b9f5a9 Allow sending stanzas on session_end.
May set self.disconnect_wait=True so that all disconnect
calls wait for the send queue to empty, unless explicitly
overridden with wait=False.

The session_end now fires before closing the socket so
that final stanzas may be sent, such as unavailable presences
for components.
2011-12-09 23:56:39 -08:00
Lance Stout
65dbddb6b6 Fix logging when loading plugins. 2011-12-09 20:57:08 -08:00
Lance Stout
2a67a31120 Prevent hang when terminating during delayed connection. 2011-12-07 22:16:58 -08:00
Lance Stout
a720c3348b Updated last bit of core files to use new API format. 2011-12-05 20:37:47 -08:00
Lance Stout
79ac60b6e8 Fix example boilerplate code syntax. 2011-12-05 08:57:57 -08:00
Lance Stout
e01c2d222a More doc updates 2011-12-05 08:55:05 -08:00
Lance Stout
8922e2050a Update the API docs for XMLStream 2011-12-04 20:35:17 -08:00
Lance Stout
a85891c611 Add API docs for the scheduler 2011-12-04 16:43:05 -08:00
Lance Stout
2586fdffda Update api docs for handlers and matchers 2011-12-04 16:26:14 -08:00
Lance Stout
c9dc9ec11e Update supported XEP list 2011-12-04 15:39:49 -08:00
Lance Stout
b9332142c9 Update api docs for JID 2011-12-04 13:42:46 -08:00
Lance Stout
b7b53362e1 Ensure that adhoc command clients have form plugin registered.
The form plugin was being registered on first use for providers,
but not for clients receiving the form.

NOTE: Use of non-form payloads will have this issue - adhoc command
      clients will need to have an expectation beforehand of what
      the command payload will be to properly load stanza plugins.
2011-12-04 01:24:35 -08:00
Lance Stout
68cf66a5fe Ensure that saving a roster item includes the correct subscription value.
Fixes issue #118
2011-11-28 15:00:35 -08:00
Florent Le Coz
4eb7eeb40f Send the encoded data (bytes) and not the str, on the socket. 2011-11-25 01:45:43 +08:00
Lance Stout
a1d64fa215 Experimental support for handling SSL write errors. 2011-11-23 23:59:05 -08:00
Lance Stout
5f44c0e678 Add docs for filesocket 2011-11-22 16:33:38 -08:00
Lance Stout
b87c4d786d Update tostring docs, plus more doc cleanup 2011-11-22 16:25:33 -08:00
Lance Stout
329b0df3f6 Some more docs house cleaning 2011-11-22 15:25:24 -08:00
Lance Stout
6906c15e8e Update docs for tostring 2011-11-22 15:25:02 -08:00
Lance Stout
ff5421cefc Moar docs! 2011-11-21 23:28:19 -08:00
Lance Stout
4498e992a2 Add more stanzabase docs 2011-11-21 23:17:39 -08:00
Lance Stout
2d610dfdc8 Fix stream handler test for multiple handlers to exist properly. 2011-11-21 22:03:43 -08:00
Lance Stout
2b0a05ee32 Update stanzabase docs 2011-11-21 21:51:19 -08:00
Lance Stout
bc2d0ee9a8 Update docs index 2011-11-20 17:29:54 -08:00
Lance Stout
862a2a1440 Ensure that reconnection happens properly after connection loss.
Calling reconnect() simultaneously from multiple threads (like when
using XEP-0199 keepalive) could break because the connection state
can transition and break the state expectations in one of the
reconnect() calls.
2011-11-20 12:18:37 -08:00
Lance Stout
fba60ffff1 Convert daemon threads back into normal threads.
This may need to be reverted if CTRL-C handling breaks, but everything
works fine so far in testing.

Resolves issue #95.
2011-11-20 12:17:35 -08:00
Lance Stout
d1a945a305 Tidy up logging some more 2011-11-19 19:18:43 -08:00
Lance Stout
685b9ab102 Fix logging exceptions from formatting issues. 2011-11-19 19:08:27 -08:00
Lance Stout
24f27c0fe3 Pass generic connection errors to XMLStream.exception() 2011-11-19 19:01:07 -08:00
Lance Stout
3019c82d8a Use a list comprehension instead of filter() to work with Python3. 2011-11-19 18:49:18 -08:00
Lance Stout
f9d0b55ca3 Add unit test for copying stanzas when passed to events. 2011-11-19 18:43:38 -08:00
Lance Stout
b54cc97e4c Merge remote-tracking branch 'vijayp/master' into HEAD
Conflicts:
	examples/ping.py
	sleekxmpp/basexmpp.py
	sleekxmpp/clientxmpp.py
	sleekxmpp/features/feature_bind/bind.py
	sleekxmpp/features/feature_mechanisms/mechanisms.py
	sleekxmpp/plugins/gmail_notify.py
	sleekxmpp/plugins/jobs.py
	sleekxmpp/plugins/xep_0009/remote.py
	sleekxmpp/plugins/xep_0009/rpc.py
	sleekxmpp/plugins/xep_0012.py
	sleekxmpp/plugins/xep_0045.py
	sleekxmpp/plugins/xep_0050/adhoc.py
	sleekxmpp/plugins/xep_0078/legacyauth.py
	sleekxmpp/plugins/xep_0085/chat_states.py
	sleekxmpp/plugins/xep_0199/ping.py
	sleekxmpp/plugins/xep_0224/attention.py
	sleekxmpp/xmlstream/handler/waiter.py
	sleekxmpp/xmlstream/matcher/xmlmask.py
	sleekxmpp/xmlstream/xmlstream.py

Conflicts:
	examples/ping.py
	sleekxmpp/basexmpp.py
	sleekxmpp/clientxmpp.py
	sleekxmpp/features/feature_bind/bind.py
	sleekxmpp/features/feature_mechanisms/mechanisms.py
	sleekxmpp/plugins/gmail_notify.py
	sleekxmpp/plugins/jobs.py
	sleekxmpp/plugins/xep_0009/remote.py
	sleekxmpp/plugins/xep_0009/rpc.py
	sleekxmpp/plugins/xep_0012.py
	sleekxmpp/plugins/xep_0045.py
	sleekxmpp/plugins/xep_0050/adhoc.py
	sleekxmpp/plugins/xep_0078/legacyauth.py
	sleekxmpp/plugins/xep_0085/chat_states.py
	sleekxmpp/plugins/xep_0199/ping.py
	sleekxmpp/plugins/xep_0224/attention.py
	sleekxmpp/xmlstream/handler/waiter.py
	sleekxmpp/xmlstream/matcher/xmlmask.py
	sleekxmpp/xmlstream/xmlstream.py
2011-11-19 18:23:26 -08:00
Vijay Pandurangan
e3b9d5abbf double copy 2011-11-19 16:03:17 -08:00
Vijay Pandurangan
2332970cf2 elide unnecessary copy 2011-11-19 16:02:41 -08:00
Vijay Pandurangan
48af3d3322 remove unnecessary copies when only one handler matches. This was taking up ~ 15% of CPU on moderate load. 2011-11-19 15:59:38 -08:00
Lance Stout
429c94d6a9 Tidy up logging calls. 2011-11-19 12:07:57 -08:00
Vijay Pandurangan
deb52ad350 This change stops sleekxmpp from spending huge amounts of time unnecessarily computing logging data that may never be used. This is a HUGE performance improvement; in some of my test runs, unnecessary string creation was accounting for > 60% of all CPU time.
Note that using % in a string will _always_ perform the sting substitutions, because the strings are constructed before the function is called. So log.debug('%s' % expensiveoperation()) will take about the same CPU time whether or not the logging level is DEBUG or INFO. if you use , no substitutions are performed unless the string is actually logged
2011-11-20 03:39:05 +08:00
Vijay Pandurangan
6f3cc77bb5 This change stops sleekxmpp from spending huge amounts of time unnecessarily computing logging data that may never be used. This is a HUGE performance improvement; in some of my test runs, unnecessary string creation was accounting for > 60% of all CPU time.
Note that using % in a string will _always_ perform the sting substitutions, because the strings are constructed before the function is called. So log.debug('%s' % expensiveoperation()) will take about the same CPU time whether or not the logging level is DEBUG or INFO. if you use , no substitutions are performed unless the string is actually logged
2011-11-19 11:30:44 -08:00
Lance Stout
1baf139ca4 Bump next release version to 1.0-RC4 2011-11-18 16:40:17 -08:00
Lance Stout
7945b3e738 Remove the config_component example in favor of echo_component.
The roster portion of the example is too outdated.
2011-11-18 16:26:02 -08:00
Lance Stout
d4c1ff5309 Also fire changed_status when the status text changes for a resource. 2011-11-18 13:57:41 -08:00
Lance Stout
22868c3924 Fix changed_status event
Once again only fires when a resource's presence show value changes.
2011-11-18 13:39:02 -08:00
Lance Stout
2de1be188c Add echo component example. 2011-11-17 12:25:56 -08:00
Lance Stout
9faecec2db Simplify boilerplate example. 2011-11-14 12:00:21 -08:00
Lance Stout
5d7111fe3b Update list of stable releases. 2011-11-14 11:46:07 -08:00
Lance Stout
0c86f8288d No need to continue processing loop if an error ocurred and auto_reconnect=False. 2011-11-14 11:21:05 -08:00
Lance Stout
5a6a65fd9f Fix typo 2011-11-14 11:20:53 -08:00
Lance Stout
43c4d23896 Explicitly test for inequality in JIDs.
Fixes issue #113
2011-11-14 09:15:43 -08:00
Lance Stout
9f9e8db814 Add use_ssl parameter to ClientXMPP.connect 2011-11-11 01:52:18 -08:00
Lance Stout
b8efcc7cf0 Don't just call self.disconnect in self.reconnect.
It messes up the auto_reconnect value and causes the XML processing
loop to spin wildly with errors on a stream disconnect.
2011-11-08 19:23:53 -08:00
Lance Stout
2f29d18e53 Use setuptools if available. 2011-11-08 07:01:16 -08:00
Lance Stout
888e286a09 Continue trying to reconnect, even if the attempt fails.
The transition from disconnected to connected states must be done in a
loop in case the transition fails, not just once and hope it worked.
2011-11-07 01:13:34 -08:00
Lance Stout
1a93a187f0 Fix a crash when removing a contact.
Original author: louiz
2011-11-06 08:33:03 -08:00
Lance Stout
a8d5da5091 Restore original behaviour for auto_authorize and auto_subscribe.
The change to using the new roster broke the original auto_* values
and used per-roster versions. The original auto_* values will now set
the behaviour globally. Use the per-roster values to override for a
specific JID.
2011-11-06 08:25:29 -08:00
Lance Stout
e2720fac9e FIX SCRAM-SHA-1-PLUS
The mechanism name was being correctly de-plussed, but then we used the
original, -PLUS, name to extract the hash, finding SHA-1-PLUS and therefore
finding no match.

Test-Information:

Tested with Sleek against an Isode M-Link with SCRAM-SHA-1-PLUS available.

Author: dwd
2011-10-27 15:16:54 -04:00
Lance Stout
4374729f20 Update the docs for XEP-0060 publish method. 2011-10-11 20:37:50 -04:00
Lance Stout
87999333cb Fix MUC methods to optionally specify the sending JID.
Should fix issue #107
2011-10-10 11:31:03 -04:00
Lance Stout
335dc2927b Break reference cycle to fix potential memory leaks for callback handlers. 2011-10-08 17:31:30 -04:00
Lance Stout
ccbef6b696 Fix typos in the roster update method. 2011-10-07 18:13:50 -04:00
Lance Stout
3e384d3cfe XEP-0009 will likely be updated to use <base64 /> instead of <Base64 />
Both are supported when reading, but <base64 /> will be used for output.
2011-10-05 12:09:50 -04:00
Lance Stout
e33949c397 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-10-04 10:37:42 -04:00
Lance Stout
eccac859ad Fix missing import statement.
Fixes issue #105
2011-10-04 10:36:52 -04:00
Lance Stout
7dd586f2fd Merge pull request #104 from correl/develop
Make RPC events threaded
2011-10-03 13:19:16 -07:00
Correl Roush
3607c5b792 Make RPC events threaded
Allows, for example, an RPC service to make remote RPC calls with its
own connection without blocking its own thread waiting for the result.
2011-10-03 14:32:48 -04:00
Lance Stout
e37adace62 Allow SASL mechanism to be set when creating a ClientXMPP instance.
Instead of using:

    ClientXMPP(jid, password, plugin_config={
        'feature_mechanisms': {'use_mech': 'SOME-MECH'}})

You can use:

    ClientXMPP(jid, password, sasl_mech='SOME-MECH')

If you need to change the mechanism after instantiation, use:

    xmpp['feature_mechanisms'].sasl.mech = 'SCRAM-MD5'
2011-09-28 22:48:30 -04:00
Lance Stout
d10f591bf4 Expand live stream testing capabilities. 2011-09-28 17:26:29 -04:00
Lance Stout
262da78ca7 Fix del_event_handler for Python3 (different semantics for filter()).
Fixes issue #103
2011-09-23 12:03:49 -04:00
Lance Stout
0b83edf439 Fix regression for handling the case where the server terminates the stream.
The processing loop was continuing to call __read_xml after </stream>
was received, which caused SyntaxErrors (can't find starting element).

This should fix issue #102
2011-09-22 01:32:44 -04:00
Nathan Fritz
cf7fcf496e SyntaxError requires a restart 2011-09-19 11:53:09 -07:00
Lance Stout
1765271f84 Make get_node_config block by default. 2011-09-02 11:52:56 -07:00
Lance Stout
0ec79f8dc3 Tweak setup.py, and bump dev version to RC3. 2011-09-01 16:47:30 -07:00
Lance Stout
6f72c05ebf Add whitespace keepalive option.
May be disabled by setting:
    self.whitespace_keepalive = False

The keepalive interval can be adjusted using:
    self.whitespace_keepalive_interval = 300

The default interval is 5min.
2011-09-01 16:24:09 -07:00
Nathan Fritz
20cacc84ba remove ping schedule on disconnect 2011-09-01 15:51:43 -07:00
Lance Stout
24a14a0284 Mark pubsub state stanzas as non-standard. 2011-09-01 15:29:05 -07:00
Lance Stout
982c2d9b83 Add tests for pubsub error stanzas 2011-09-01 15:26:54 -07:00
Lance Stout
efa4a9b330 More stanza cleanup for pubsub. 2011-09-01 14:20:58 -07:00
Lance Stout
39ec1cff19 Some more minor cleanup. 2011-09-01 14:03:11 -07:00
Lance Stout
24c5f8d374 Clean up pubsub#event stanzas. 2011-09-01 14:01:58 -07:00
Lance Stout
d6b0158ddb Clean up pubsub#owner stanzas. 2011-09-01 13:47:55 -07:00
Lance Stout
7e5e9542e9 Add support for notify attribute when retracting an item. 2011-09-01 13:36:11 -07:00
Lance Stout
d7fc2aaa9c Add ability to get global/node default subscription options. 2011-09-01 13:25:35 -07:00
Lance Stout
8471a485d1 Clean up pubsub stanzas. 2011-09-01 13:12:26 -07:00
Lance Stout
462b375c8f Owners can modify subscriptions/affiliations. With tests.
94% coverage for the main pubsub plugin! (91% including stanzas)
2011-09-01 12:09:24 -07:00
Lance Stout
afbd506cfc Users can retrieve their affiliations now, with tests. 2011-09-01 11:30:55 -07:00
Lance Stout
ec01e45ed1 Add ability for a user to get retrieve subscriptions, with tests. 2011-09-01 11:19:25 -07:00
Lance Stout
993829b23f Add tests for pubsub subscription options. 2011-09-01 10:44:14 -07:00
Lance Stout
002257b820 Add tests for retrieving pubsub items. 2011-09-01 09:27:10 -07:00
Lance Stout
0af35c2224 Fix memory reference bugs. 2011-09-01 00:50:45 -07:00
Lance Stout
76bc0a2ba6 XEP-0060 v1.13 dictates publishing/retracting one item at a time. 2011-08-31 23:48:22 -07:00
Lance Stout
d2dc4824ee Simplify pubsub tests.
We don't really care about empty responses, so let's use block=False.
2011-08-31 21:52:17 -07:00
Lance Stout
3f9ca0366b Add test for purging a pubsub node. 2011-08-31 21:09:25 -07:00
Lance Stout
b68785e19e Retract stanzas are behaving oddly when using stanza values. 2011-08-31 16:03:32 -07:00
Lance Stout
a1bbb719e1 Test publishing multiple items, and with options. 2011-08-31 15:04:46 -07:00
Lance Stout
46f23f7348 Test publishng an item with options. 2011-08-31 14:55:37 -07:00
Lance Stout
09252baa71 Test publishing a single item. 2011-08-31 14:31:20 -07:00
Lance Stout
3623a7a16a More pubsub unit tests! 2011-08-31 14:05:29 -07:00
Lance Stout
cc504ab07c Fix pubsub get_items.
- item_ids checked for None
- pubsub node is set
2011-08-31 10:56:43 -07:00
Lance Stout
2500a0649b Fix requesting pubsub node configuration, and add tests.
- <default /> doesn't have a type attribute in the XEP
- <configure /> isn't used anymore for requesting default configuration
2011-08-31 10:43:33 -07:00
Lance Stout
5ec4e4a026 Added pubsub error stanza.
iq['error']['pubsub']['condition']
iq['error']['pubsub']['unsupported']
2011-08-31 00:42:37 -07:00
Lance Stout
c3df4dd052 Create a tox config for automating tests for different Python versions.
To use:
    sudo pip install tox
    tox
2011-08-31 00:00:12 -07:00
Lance Stout
730c3fada0 Add tests for pubsub unsubscribe. 2011-08-30 23:18:13 -07:00
Lance Stout
628978fc8c Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-08-30 23:11:11 -07:00
Nathan Fritz
7fb9d68714 fixed form accessors in pubsub stanzas 2011-08-30 23:10:13 -07:00
Lance Stout
e0a1c477d0 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-08-30 23:03:51 -07:00
Nathan Fritz
b70565720f fixed test further... but now I have an out of order problem 2011-08-30 23:03:04 -07:00
Lance Stout
33ac0c9dd6 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-08-30 22:45:08 -07:00
Nathan Fritz
4699bdff60 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-08-30 22:44:34 -07:00
Nathan Fritz
354641a3ce added publish-options element 2011-08-30 22:44:19 -07:00
Lance Stout
58a43e40c7 Get/set pubsub subscription options. 2011-08-30 22:27:21 -07:00
Lance Stout
6b7fde10d3 Test pubsub subscribe. 2011-08-30 22:27:02 -07:00
Lance Stout
13fdab0139 Test and fix XEP-0060 delete_node() 2011-08-30 21:57:11 -07:00
Lance Stout
2ce617b2ce Fix typo 2011-08-30 09:24:46 -07:00
Lance Stout
63e0496c30 Finish up all major actions in the current XEP-0060.
Still need tests and docs.
2011-08-29 23:05:14 -07:00
Lance Stout
850e3bb99b Stub out missing functionality for pubsub 2011-08-29 21:38:41 -07:00
Lance Stout
2d90deb96a The ifrom parameter doesn't need special treatment. 2011-08-26 22:06:32 -07:00
Lance Stout
3fb3f63e51 Add docs + extended Iq send arguments to pubsub methods. 2011-08-26 16:57:37 -07:00
Lance Stout
d12949ff1c Fix typos in XEP-0060, start of docs and tests. 2011-08-26 12:14:57 -07:00
Lance Stout
e3e985220e Simplify the main process loop. 2011-08-25 17:08:20 -07:00
Lance Stout
802dd8393d Make the timeout for event queue checks configurable.
Now defaults xmlstream.WAIT_TIMEOUT, and settable with
self.wait_timeout.

The new default timeout is 1sec instead of 5sec.
2011-08-25 16:45:34 -07:00
Lance Stout
fe6bc31c60 Added XMLStream.configure_dns.
This can be overridden to do custom configuration for the DNS resolver,
or any other DNS related tasks such as calling the system's res_init().
2011-08-25 16:18:26 -07:00
Lance Stout
2162d6042e Session timeout now defaults to 45sec, but can be adjusted.
e.g.

    self.session_timeout = 15

It is also managed by XMLStream instead of ClientXMPP now.
2011-08-25 15:40:13 -07:00
Lance Stout
b8a4ffece9 Handle sending stanzas in chunks if the socket has poor performance. 2011-08-25 15:08:45 -07:00
Lance Stout
d929e0deb2 Shutdown socket before closing. 2011-08-25 13:48:43 -07:00
Lance Stout
4c08c9c524 Update scheduler with locks and ability to remove tasks.
Scheduled tasks must have a unique name.
2011-08-25 13:34:30 -07:00
Lance Stout
63b8444abe Add overridable method self.configure_socket().
Allows for setting app specific socket timeouts and other socket options.
2011-08-25 00:22:26 -07:00
Lance Stout
82546d776d Fix tests in Python3. 2011-08-25 00:21:53 -07:00
Lance Stout
84f9505a8d Fix handling of DNS exceptions. 2011-08-24 22:40:57 -07:00
Lance Stout
ede59ab40e Clean and get setup.py working once and for all.
Fixes:
    README.rst now included
    Double line spacing removed from long_description
    Source package now includes tests, examples, etc using Manifest.in
    README.rst typos fixed
    Added README.rst section on installing dnspython for Python3
    Version bumped to RC2
    Version is now taken from sleekxmpp.version.__version__ without
        having to pull in the entire library
    Added 'test' command for setup.py
    Simplified testall.py
    Docs build cleanly from source package after installation
2011-08-24 22:09:02 -07:00
Lance Stout
2a80824076 Remove extra debugging code that made it into a commit. 2011-08-23 14:14:21 -07:00
Lance Stout
f92f96325a Make Iq exceptions more discoverable and simpler to use.
IqError and IqTimeout now extend XMPPError, so if you don't care
about the difference, you can use:

    try:
        self.do_something_with_iqs()
    except XMPPError:
        # Error? Timeout? I don't care!
        pass

If you do need to distinguish between timeouts and error replies,
you can still continue to use:

    try:
        self.do_somethin_with_iqs()
    except IqError as err:
        pass
    except IqTimeout:
        pass

If you don't catch any Iq errors and you're processing a stanza
then an error response will be sent, just like normal if you raise
XMPPError or any other exception, except that the error messages
will be generic to prevent leaking too much information.
2011-08-19 01:04:20 -07:00
Lance Stout
b98555c512 Update the README
Now includes how to generate the docs, run tests, and the basic
boilerplate for Sleek based projects.
2011-08-18 16:32:06 -07:00
Lance Stout
e02a42a008 Route all unhandled exceptions through XMLStream.exception.
Or through an equivalent override.
2011-08-18 16:12:51 -07:00
Lance Stout
3e51126e18 PEP8 edits 2011-08-18 02:46:48 -07:00
Lance Stout
a714fa82b2 Remove extra, unhelpful presence debug log. 2011-08-18 02:46:08 -07:00
Lance Stout
e86e6eae81 Up the timeout to 30sec instead of 10sec. 2011-08-18 01:10:25 -07:00
Lance Stout
592c25f352 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-08-18 01:07:21 -07:00
Lance Stout
015f662249 Update examples to work with Python3 (raw_input vs input) 2011-08-18 01:06:59 -07:00
Lance Stout
8d998d71a3 Update README (renamed to README.rst so Github will render it) 2011-08-18 01:04:59 -07:00
Nathan Fritz
f75b6bf955 added inline documentation for new dns methods 2011-08-18 01:04:01 -07:00
Nathan Fritz
fb78bf0996 fixed manual address definition 2011-08-18 00:59:27 -07:00
Lance Stout
cd7cd30b4c Fix exceptions for Python3 2011-08-18 00:47:07 -07:00
Nathan Fritz
4ea22ff69b Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-08-18 00:35:37 -07:00
Nathan Fritz
3853898ab3 DNS is now properly checked and different answers are tried for each reconnect until exhausted 2011-08-18 00:35:18 -07:00
Lance Stout
7d8aa4157b Add an example for dumping the roster to the command line. 2011-08-18 00:08:52 -07:00
Lance Stout
3fc20e10f5 Add some convenience methods to rosters.
Can now use len(self.client_roster) to get the number of JIDs in
the roster, and self.client_roster.groups() to get a dict of
groups and the JIDs in those groups.
2011-08-18 00:07:37 -07:00
Lance Stout
004eabf809 Update plugins that use Iq stanzas to work with new exceptions. 2011-08-17 21:30:47 -07:00
Lance Stout
62230fc970 Return '' instead of None from form fields with no values. 2011-08-17 21:22:03 -07:00
Lance Stout
961668d420 Add guide for sending a message and then disconnecting. 2011-08-17 21:21:37 -07:00
Lance Stout
01061a0355 More documentation!
Finished the echo bot quickstart.

Added placeholders for other guides we need.
2011-08-13 20:58:53 -07:00
Lance Stout
9fdd85d9f1 I've seen people complain about Sleek taking so long to disconnect.
Added logging to say that we're waiting for the server to end the stream
from its end.
2011-08-13 08:58:07 -07:00
Lance Stout
331db30f8f Add form.field back in for backwards compatibility. 2011-08-13 08:34:23 -07:00
Lance Stout
017d7ec62b Add tests for setting a form's type to 'submit' or 'cancel'.
Form fields now remember their current type if the type is deleted. This
allows for fields to properly format their values if set after the form
has been changed to the 'submit' type.
2011-08-13 01:28:18 -07:00
Lance Stout
76826b5495 The todo1.0 list isn't needed any longer :)
The last few plugins that were in the list will be cleaned up in 1.1
or 1.2 - XEP-0012, XEP-0009, XEP-0033, XEP-0045, and gmail_notify.
2011-08-13 00:16:36 -07:00
Lance Stout
de315ff6d8 Fix typo. 2011-08-13 00:16:25 -07:00
Lance Stout
c26b716164 Update XEP-0050 to use new IQ exceptions.
IqError is now caught and forwarded to the command error handler referenced
in the session.

Errors are now caught and processed by the session's error handler
whether or not the results Iq stanza includes the <command> substanza.

Added the option for blocking command calls. The blocking option is set
during start_command with block=True. Subsequent command flow methods use
session['block'] to determine their blocking behaviour.

If you use blocking commands, then you will need to wrap your command calls
in a try/except block for IqTimeout exceptions.
2011-08-13 00:10:06 -07:00
Lance Stout
dcaddb8042 Include new XEP-0004 directories in setup.py 2011-08-12 23:56:14 -07:00
Lance Stout
5ef197e5fd Start of docs for 1.0 2011-08-12 17:33:32 -07:00
Lance Stout
52ed02bd06 Update .gitignore 2011-08-12 17:22:27 -07:00
Lance Stout
bd427849fb Reduce the maximum delay between connection retries to 10min. 2011-08-12 17:17:05 -07:00
Lance Stout
127d7acb91 Missing commas in setup.py 2011-08-12 16:53:08 -07:00
Lance Stout
484efff156 Merge branch 'develop' into roster
Conflicts:
	setup.py
	sleekxmpp/clientxmpp.py
2011-08-12 16:47:58 -07:00
Nathan Fritz
8f1d0e7a79 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-08-12 16:36:03 -07:00
Nathan Fritz
88184ff955 fixed indenting and merged in exceptions branch 2011-08-12 16:35:36 -07:00
Nathan Fritz
bd8c110f00 Merge branch 'exceptions' into develop 2011-08-12 16:35:15 -07:00
Nathan Fritz
0050c51124 updated pubsub plugin to use stanzas 2011-08-12 16:32:09 -07:00
Lance Stout
9b7ed73f95 Reorganize XEP-0004.
Changes:
    May now use underscored method names
    form.field is replaced by form['fields']
    form.get_fields no longer accepts use_dict parameter, it always
        returns an OrderedDict now
    form.set_fields will accept either an OrderedDict, or a list
        of (var, dict) tuples as before.
    Setting the form type to 'submit' will remove extra meta data
        from the form fields, leaving just the 'var' and 'value'
    Setting the form type to 'cancel' will remove all fields.
2011-08-11 21:59:55 -07:00
Nathan Fritz
a189cb8333 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-08-10 13:37:49 -07:00
Nathan Fritz
0d4825d3ea added send_client example 2011-08-10 13:37:36 -07:00
Lance Stout
156b3200e3 Don't include ping stanza in the ping result. 2011-08-10 09:05:43 -07:00
Lance Stout
572becad44 Enable forcing a specififc SASL mech:
xmpp = ClientXMPP(jid, password, {
    'feature_mechanisms': {'use_mech':'PLAIN'}})
2011-08-09 00:51:49 -07:00
Lance Stout
75f23d1130 Fix XEP-0078 using the new stream feature workflow.
Honestly, this is mainly just a demo/proof of concept that we
can handle dependencies and ordering issues with stream features.

DON'T use XEP-0078 if you are able to use the normal SASL method,
which should be the case unless you are dealing with a very old
XMPP server implementation.
2011-08-06 12:30:56 -07:00
Lance Stout
e83fae3a6f Save the stream ID when the stream starts. 2011-08-06 00:44:32 -07:00
Lance Stout
5be5b8c02b If no config for a plugin is given, try using self.plugin_config.
Sleek loads a few plugins by default, which made it difficult to
configure or even disable them.

Now, if a plugin is registered without any configuration, then
sleek will try finding a configuration in self.plugin_config.
2011-08-06 00:41:14 -07:00
Lance Stout
6c4cb2bf00 Merge branch 'master' into develop
Adds hotfix for ANONYMOUS mech support.

Conflicts:
	sleekxmpp/__init__.py
2011-08-05 14:08:32 -07:00
Lance Stout
148a23579c Hotfix for ANONYMOUS mech support.
Updates version to 1.0-Beta6.1
2011-08-05 14:06:58 -07:00
Lance Stout
ea95811c4c The next release will be 1.0 RC1 2011-08-05 09:01:50 -07:00
Lance Stout
08cb5f42e7 Update the info in setup.py
I thought I had done this a long time ago, but it must have been in a
lost branch. *shrug*

It's too late for Beta6, so I've manually updated the PyPI entry.
2011-08-05 09:00:55 -07:00
Lance Stout
9abf37bbd1 Ignore Manifest and dist dir in git. 2011-08-05 09:00:42 -07:00
Lance Stout
168203c94d Update 1.0 todo list.
Most of these items will probably be pushed to 1.1
2011-08-04 22:42:54 -07:00
Lance Stout
47bc50d9fb Cosmetic PEP8 fixes. 2011-08-04 22:37:22 -07:00
Lance Stout
93a4a3f8a0 Fix Python3 issue with dict.has_key() 2011-08-04 22:36:23 -07:00
Lance Stout
940e3eba35 Make sure setup.py has all of the plugins.
XEP-0199 got dropped during a cut/paste for 203 and 204.
2011-08-04 21:56:35 -07:00
Lance Stout
b7cd119b0c Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-08-04 21:50:14 -07:00
Nathan Fritz
7f90de887a added block as process option and updated documentation. added typical use example to ClientXMPP. 2011-08-04 21:49:32 -07:00
Lance Stout
6c8a135612 Fix imports using __all__. 2011-08-04 21:49:15 -07:00
Lance Stout
caec2976d7 Fix Python3 bug.
Use int() instead of long()
2011-08-04 20:34:23 -07:00
Lance Stout
4d8933abdf Actually, we can work around needing dateutil.
If dateutil is present, we'll use that. If not, we'll use
some regexes from the fixed_datetime module.
2011-08-04 20:22:07 -07:00
Lance Stout
6eac0606cf Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-08-04 19:07:04 -07:00
Lance Stout
89cffd43f4 Merge branch 'develop' into roster
Conflicts:
	setup.py
2011-08-04 11:52:17 -07:00
Lance Stout
b9764cc120 Clean up and unify tostring once and for all.
Packaging for Python3 just got easier.
2011-08-04 11:41:36 -07:00
Lance Stout
7cd39a6aad Fix imports for xep_0060 2011-08-04 11:38:14 -07:00
Lance Stout
a8f57d012f Let's make sure licenses can be seen and checked. 2011-08-04 00:41:02 -07:00
Lance Stout
a0767f6af6 Sadly, dateutil is not actually part of the standard lib.
Thus, using the XEP-0082 and XEP-0202 introduces a dependency
on the dateutil package (installable using pip install python-dateutil).

Maybe we'll be able to rework how these plugins work to avoid
needing dateutil, but for now this will have to do.
2011-08-04 00:07:30 -07:00
Nathan Fritz
9ffdba8643 the great xep_0060 re-organization in preperation for rewrite 2011-08-03 23:56:24 -07:00
Lance Stout
9591cd3a7e Merge branch 'stream_features' into develop 2011-08-03 18:35:01 -07:00
Nathan Fritz
afeb8a679a updated setup.py to include stream features plugins 2011-08-03 18:30:46 -07:00
Nathan Fritz
db92fa2330 started transition to xep_0060 rewrite 2011-08-03 18:11:00 -07:00
Lance Stout
d94517d9ca Merge branch 'develop' into stream_features 2011-08-03 17:37:15 -07:00
Lance Stout
0bec040cfe Make sure to keep David Cridland's license and copyright. 2011-08-03 17:08:45 -07:00
Lance Stout
3918ddb075 Merge branch 'develop' into stream_features 2011-08-03 17:02:33 -07:00
Lance Stout
d4091dbde6 Integrate a modified version of Dave Cridland's Suelta SASL library. 2011-08-03 17:00:51 -07:00
Lance Stout
d7fe724145 Merge branch 'develop' into exceptions 2011-07-27 19:36:04 -07:00
Lance Stout
ad978700fc Merge branch 'develop' into roster 2011-07-27 19:35:42 -07:00
Lance Stout
e022b2a36c Add support for HTTP Proxy connections. 2011-07-27 19:35:03 -07:00
Lance Stout
ad032e5ed7 Fix error with DNS selection.
Missed a renaming of 'priority' to 'item'
2011-07-27 18:40:57 -07:00
Kim Alvefur
45412fd404 Do a weighted choice among the highest prioritized items based on weight instead of a weighted choice based on priorities. 2011-07-16 11:00:59 +08:00
Lance Stout
ccc6ab1281 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-07-05 21:30:16 -07:00
Lance Stout
712da4c46e Add test to check that presence events are firing. 2011-07-05 21:29:31 -07:00
Lance Stout
b2d42b1d6c Test no longer applies to latest version of XEP-0066. 2011-07-05 11:18:46 -07:00
Lance Stout
1d22a04721 Added support for custom OOB transfer handlers.
Accepting download requests can be done using:

    self['xep_0066'].register_url_handler(handler=self.oob_download)
    # Add jid=... to specify a handler for a particular JID for a
    # componenent.

    def oob_download(self, iq):
        if iq['from'] not in self.custom_oob_whitelist:
            raise XMPPError('not-authorized')
        try:
            data = urllib2.urlopen(iq['oob_transfer']['url'])
            file = open('oob_download', 'w+')
            file.write(data.read())
            file.close()
            data.close()
        except:
            raise XMPPError('item-not-found')
2011-07-05 09:22:17 -07:00
Lance Stout
5efb170e1d Added wait parameter to disconnect.
If wait=True, then the disconnect call will block until
the send queue has emptied.

WARNING: Using wait=True when more stanzas are being added to the
queue than can be processed such that the queue is never empty
will cause the disconnect call to block indefinitely without actually
disconnecting.
2011-07-04 18:47:57 -07:00
Lance Stout
cccccdcc0a Add logging to XEP-0066. 2011-07-03 20:39:03 -07:00
Lance Stout
8d384ce44f Added XEP-0224 Attention plugin. 2011-07-03 20:37:21 -07:00
Lance Stout
4d6e7c7dbb Update version number to beta6.
XEP-0092 now uses sleekxmpp.__version__ as a default version number.
2011-07-03 15:47:12 -07:00
Lance Stout
9c5885c6b6 Let XEP-0202 specify the local timezone offset in the config. 2011-07-03 15:41:22 -07:00
Lance Stout
ec3a14e6d9 Updated XEP-0202 plugin to new format and use XEP-0082. 2011-07-03 15:30:06 -07:00
Lance Stout
c98f5d4450 Fix some bugs in time handling.
Namely, minutes and seconds were reversed.
2011-07-03 13:41:15 -07:00
Lance Stout
2e8e542bc9 Added XEP-0203 Delayed Delivery plugin. 2011-07-03 12:43:34 -07:00
Lance Stout
7ccc67c06d Added XEP-0082 plugin.
This should make things much easier for any stanza that uses timestamps.
2011-07-03 12:21:47 -07:00
Lance Stout
20df6348a4 Merge branch 'develop' into exceptions 2011-07-03 00:39:14 -07:00
Lance Stout
48fb7006f7 Merge branch 'develop' into roster 2011-07-03 00:39:02 -07:00
Lance Stout
9a6eb333e6 Merge branch 'develop' into stream_features 2011-07-03 00:38:22 -07:00
Lance Stout
086bf89d69 Added XEP-0066: Out-of-Band Data 2011-07-03 00:36:36 -07:00
Lance Stout
0224d028e7 SASL failure event now includes the failure stanza.
Broke SASL stanzas into separate files.

Fixed typo in feature_bind.
2011-07-02 23:09:29 -07:00
Lance Stout
540d749695 Fix ordering bug when retrieving an error condition. 2011-07-02 22:50:31 -07:00
Lance Stout
219df582da It isn't 2010 anymore.
I keep forgetting to update the copyright on new code.
2011-07-02 22:49:34 -07:00
Lance Stout
b898b14b77 Use a set to track negotiated features.
Added guards to prevent renegotiating STARTTLS or SASL in cases where
servers don't behave properly.
2011-07-02 22:30:55 -07:00
Lance Stout
fba235a801 Simplify SASL mech registration.
Moved SASL registration completely to the feature plugin, instead of
keeping a portion of it in ClientXMPP.
2011-07-02 21:57:50 -07:00
Lance Stout
b0297af38d Finish cleaning up stream feature organization.
Fixed missing references that weren't caught due to leftover pyc
file allowing tests to keep working when they shouldn't have.
2011-07-02 21:43:02 -07:00
Lance Stout
04def6d925 Merge branch 'develop' into stream_features 2011-07-01 15:19:37 -07:00
Lance Stout
7057984831 Merge branch 'develop' into roster 2011-07-01 15:19:05 -07:00
Lance Stout
2a2ac73845 So using sys.excepthook to catch errors only works once.
The error bubbles through the event processing loop, breaking it and
hanging the application.

Instead, there is now a .exception(e) method on XMLStream which may
be overridden or reassigned that will receive all unhandled exceptions
(read: not XMPPError) from event and stream handlers.
2011-07-01 15:18:10 -07:00
Lance Stout
634f5d691b Continued reorganization and streamlining. 2011-07-01 14:45:55 -07:00
Lance Stout
754ac5092a Reorganize features into plugins. 2011-06-30 15:40:22 -07:00
Lance Stout
9ed972ffeb Fix SASL mechanism selection bug.
ANONYMOUS was being treated as PLAIN, mechanism was being chosen
purely from supported mechanisms, not those provided by the server.

Broke nested handler methods into top-level methods.
2011-06-29 14:05:27 -07:00
Lance Stout
3b1f3fddf0 Reorganized stream level stanzas. 2011-06-28 11:06:44 -07:00
Lance Stout
fa716457a5 Merge branch 'develop' into stream_features 2011-06-20 16:27:55 -07:00
Lance Stout
847510c6b5 Merge branch 'develop' into exceptions 2011-06-20 16:27:39 -07:00
Lance Stout
774e0f2022 Merge branch 'develop' into roster 2011-06-20 16:27:25 -07:00
Lance Stout
d8d9e8df16 Fix stanza clobbering when replying to errors.
If a stanza handler raised an exception, the exception was processed
and replied by the modified stanza, not a stanza with the original
content.

A copy is now made before handler processing, and if an exception occurs
it is the copy that processes the exception using the original content.
2011-06-20 16:25:56 -07:00
Lance Stout
d1e12cd46f Need to store unavailable presence as last sent if broadcasted. 2011-06-18 14:39:17 -07:00
Lance Stout
adf6d49fd1 Store unavailable presence as last sent presence. 2011-06-18 14:36:19 -07:00
Lance Stout
0826a44d4b Added roster package to setup.py 2011-06-18 14:32:09 -07:00
Lance Stout
ce145b04ac Integrate roster with ClientXMPP.
Roster updates are now passed through to the roster when using
self.update_roster, etc.
2011-06-16 16:09:15 -07:00
Lance Stout
29d775e675 Integrate roster with BaseXMPP.
Last sent stanzas are saved regardless of if the roster is used
directly or self.send_presence
2011-06-16 16:03:31 -07:00
Lance Stout
251a47db8c Split roster.py into a directory. 2011-06-16 14:15:22 -07:00
Lance Stout
e4b3a191d6 Merge branch 'develop' into roster
Conflicts:
	sleekxmpp/basexmpp.py
2011-06-15 10:57:25 -07:00
Lance Stout
393d702e77 Merge branch 'develop' into exceptions 2011-06-15 10:56:49 -07:00
Lance Stout
4efd41f1ba Merge branch 'develop' into stream_features 2011-06-15 10:56:33 -07:00
Lance Stout
58aa944a5e Fix another roster issue.
Caused by same issue of a JID being in the roster, but with an
incomplete entry.
2011-06-15 10:55:36 -07:00
Lance Stout
a3597d6deb Merge branch 'develop' into roster
Conflicts:
	sleekxmpp/basexmpp.py
2011-06-14 14:24:25 -07:00
Lance Stout
8fada4d015 Merge branch 'develop' into exceptions 2011-06-14 14:18:40 -07:00
Lance Stout
5d11ab269d Merge branch 'develop' into stream_features 2011-06-14 14:10:52 -07:00
Lance Stout
dd41a85efc Fix issue with components and roster.
If the roster contained a JID, but not any resource presence data, then
an error would occur when accessing self.roster[jid]['presence'].
2011-06-14 14:03:54 -07:00
Lance Stout
6d59f55fd4 Merge branch 'develop' into exceptions 2011-06-10 15:15:01 -07:00
Lance Stout
7a60e4b458 Merge branch 'develop' into stream_features 2011-06-10 15:14:51 -07:00
Lance Stout
937dce8e65 Merge branch 'develop' into roster 2011-06-10 15:14:37 -07:00
Nathan Fritz
e2d18170b0 old xep_0050 plugin is now loadable 2011-06-10 04:14:01 +00:00
Lance Stout
e219c0f976 Added session_end event and some docs.
For now, session_end is the same as disconnected, but once support is
added later for stream management, the two events will become distinct.

Plugins should add handlers for session_end for cleaning any session
state.
2011-06-08 10:24:25 -07:00
Lance Stout
5c1562f36b Merge branch 'develop' into exceptions 2011-06-08 10:02:38 -07:00
Lance Stout
823c13707d Merge branch 'develop' into stream_features 2011-06-08 10:02:22 -07:00
Lance Stout
8eb59072b4 Merge branch 'develop' into roster 2011-06-08 10:01:52 -07:00
Lance Stout
4266ee0fa4 Fix XEP-0050 issue with Unicode string type checking. 2011-06-08 10:00:28 -07:00
Lance Stout
3a62908703 Send component handshake immediately. 2011-06-08 10:00:01 -07:00
Lance Stout
20d053807d IqTimeout now references the original sent stanza.
A little extra bit of docs for IqError.
2011-06-01 15:28:33 -07:00
Lance Stout
8aa4396e44 Begin experimental use of exceptions.
Provides IqTimeout and IqError which are raised when an Iq response
does not arrive in time, or it arrives with type='error'.
2011-06-01 15:17:22 -07:00
Lance Stout
8f9100c762 Merge branch 'develop' into stream_features 2011-06-01 15:17:13 -07:00
Lance Stout
788a5b73f9 Merge branch 'develop' into roster 2011-06-01 15:17:04 -07:00
Lance Stout
1469323350 Cache stanza if sending fails.
The stanza will be sent first once the send queue is reactivated
after session start.

Stanzas sent by skipping the queue will not be cached.
2011-06-01 15:10:44 -07:00
Lance Stout
83a73ac9b7 Merge branch 'develop' into stream_features
Conflicts:
	sleekxmpp/clientxmpp.py
2011-05-31 11:05:54 -07:00
Lance Stout
5ed27bf5f6 Merge branch 'develop' into roster 2011-05-31 10:59:14 -07:00
Lance Stout
a81162edd2 Apply connection backoff to reconnect attempts.
Backoff was only being done for the initial connection attempt
before. Now any reconnection will start with a minimum 1 sec
delay which will approximately double between attempts.
2011-05-31 10:55:15 -07:00
Lance Stout
8080b4cae2 Cleanup logging and exception handling.
The syntax and attribute errors raised during a disconnect/reconnect
attempt are now caught and produce nicer log messages.
2011-05-31 10:23:05 -07:00
Lance Stout
1735c194cd Don't use the send queue for stream initialization.
Use the parameter now=True to skip the queue when
sending Iq stanzas, or using xmpp.send().
2011-05-27 17:00:57 -07:00
Lance Stout
6997b2fbf8 Fix typo for SSL certificate use. 2011-05-27 16:39:45 -07:00
Lance Stout
b81ab97900 Add exponential backoff to connection attempts.
Delay will approximately double between attempts (random variation).

See issue #67.
2011-05-27 14:42:40 -07:00
Lance Stout
384e1a92b7 Added support for testind disconnect errors. 2011-05-27 11:01:30 -07:00
Lance Stout
ccb0eeefbd Merge branch 'develop' into stream_features 2011-05-25 15:53:33 -07:00
Lance Stout
62bdaab7c7 Merge branch 'develop' into roster 2011-05-25 15:53:24 -07:00
Lance Stout
ec9aed5b75 Fix test for get_roster().
Python2.6 has issues passing a Unicode string as a keyword name.
2011-05-25 15:52:42 -07:00
Lance Stout
9851a2a057 Merge branch 'develop' into stream_features 2011-05-20 21:41:44 -04:00
Lance Stout
bb2bc64d15 Merge branch 'develop' into roster 2011-05-20 21:40:37 -04:00
Lance Stout
7152d93dd0 Fix test timeout issue.
A better method than using time.sleep is needed.
Maybe use queue.task_done to detect when event processing
has ended? Research time!
2011-05-20 21:38:43 -04:00
Lance Stout
baa1eaf73a Fix test for Python3.
Issue of dict_keys vs list data types.
2011-05-20 21:36:09 -04:00
Lance Stout
4c7da3899e Merge branch 'develop' into roster
Conflicts:
	tests/test_stream_roster.py
2011-05-20 21:26:45 -04:00
Lance Stout
4bb226147a Make roster test a little more robust. 2011-05-20 21:19:27 -04:00
Lance Stout
4d3593ac86 Merge branch 'develop' into roster
Conflicts:
	tests/test_stream_roster.py
2011-05-20 21:12:53 -04:00
Lance Stout
c49a8e9114 Save progress 2011-05-20 17:42:40 -04:00
Lance Stout
6b274a2543 Fix double roster entry issue with Unicode.
JIDs with Unicode values were being encoded by the JID class
instead of leaving them as just Unicode strings.

It may still be a good idea to use

    from __future__ import unicode_literals

pretty much everywhere though.

Fixes issue #88.
2011-05-20 16:48:13 -04:00
Lance Stout
a269be485f Merge branch 'develop' into stream_features 2011-05-20 13:46:46 -04:00
Lance Stout
d3bd9cd31d Merge branch 'develop' into roster 2011-05-20 13:46:36 -04:00
Lance Stout
6a07e7cbe3 Handle callback return value case. 2011-05-20 13:46:12 -04:00
Lance Stout
e694e4a791 Merge branch 'stream_features' of github.com:fritzy/SleekXMPP into stream_features 2011-05-20 13:27:39 -04:00
Lance Stout
e2de82ac8d Merge branch 'develop' into stream_features
Conflicts:
	sleekxmpp/clientxmpp.py
2011-05-20 13:26:21 -04:00
Lance Stout
e3b14bc5a9 Merge branch 'develop' into roster
Conflicts:
	sleekxmpp/clientxmpp.py
	tests/test_stream_roster.py
2011-05-20 13:23:48 -04:00
Lance Stout
9f1648328f Resolve timeout errors for get_roster.
See issue #89

Using get_roster will now return the same types of values as
Iq.send. If a timeout occurs, then the event 'roster_timeout'
will be fired. A successful call to get_roster will also
raise the 'roster_received' event.

To ensure that the get_roster call was successful, here
is a pattern to follow:

    def __init__(self, ...):
        ...
        self.add_event_handler('session_start', self.session_start)
        self.add_event_handler('roster_timeout', self.roster_timeout)
        self.add_event_handler('roster_received', self.roster_received)

    def session_start(self, e):
        self.send_presence()
        self.get_roster()

    def roster_timeout(self, e):
        # Optionally increase the timeout period
        self.get_roster(timeout=self.response_timeout * 2)

    def roster_received(self, iq):
        # Do stuff, roster has been initialized.
        ...
2011-05-20 12:56:00 -04:00
Lance Stout
8e9b3d0760 Ensure that the XEP-0086 plugin is loaded.
Since the XEP-0086 plugin auto adds error code values,
it must be reliably loaded or unloaded when certain tests
are run so that stanzas may be matched. In this case, we
ensure that the plugin is used.
2011-05-13 15:28:47 -04:00
Lance Stout
b1439df72a Merge branch 'develop' into stream_features 2011-04-26 16:33:42 -04:00
Lance Stout
8e46aa7054 Merge branch 'develop' into roster 2011-04-26 16:33:32 -04:00
Lance Stout
5399fdd3a9 Add support for testing that no stanzas are sent in tests.
Use: self.send(None)
2011-04-26 16:32:58 -04:00
Nathan Fritz
8a22597180 added has_jid to roster 2011-04-15 17:43:12 -07:00
Nathan Fritz
016aac69f6 Pubsub/Unsubscribe was not getting registered 2011-04-14 17:35:20 -07:00
Nathan Fritz
e919906c8c Pubsub/Unsubscribe was not getting registered 2011-04-14 17:34:33 -07:00
Nathan Fritz
46dc6eac88 remove roster item state responsibility from clients 2011-04-14 16:27:27 -07:00
Lance Stout
ea8c40c7b6 Merge branch 'develop' into stream_features 2011-04-11 14:23:47 -04:00
Lance Stout
b9bf30e095 Merge branch 'develop' into roster 2011-04-11 14:23:39 -04:00
Lance Stout
1d891858b6 Mark scheduler thread as a daemon. 2011-04-11 14:22:32 -04:00
Lance Stout
b60c51ef13 Merge branch 'develop' into roster 2011-04-08 16:52:20 -04:00
Lance Stout
f02b0564e0 Update tests to reflect XEP-0086 correcting error codes. 2011-04-08 16:51:24 -04:00
Lance Stout
ae5ce17035 Merge branch 'develop' into stream_features 2011-04-08 16:42:02 -04:00
Lance Stout
6b05938573 Merge branch 'develop' into roster 2011-04-08 16:41:45 -04:00
Lance Stout
2e1befc8c6 Make setup.py use sleekxmpp.__version__ 2011-04-08 16:41:18 -04:00
Lance Stout
87ccd804ff Add version info.
May now use sleekxmpp.__version__ and sleekxmpp.__version_info__.
2011-04-08 16:39:39 -04:00
Lance Stout
d7ba7cc72a Use underscore method name.
Since camelcase names are aliased to the underscored name at startup,
if the underscored version is replaced later, the camelCase name does
not reflect the change.
2011-04-08 16:14:22 -04:00
Lance Stout
f125c11a81 Merge branch 'develop' into stream_features 2011-03-24 13:15:09 -04:00
Lance Stout
77601f7262 Merge branch 'develop' into roster 2011-03-24 13:15:00 -04:00
Lance Stout
d94811d81d Added new implementation for XEP-0086. 2011-03-24 13:14:26 -04:00
Lance Stout
6d45971411 Allow a stanza plugin to override a parent's interfaces.
Each interface, say foo, may be overridden in three ways:
    set_foo
    get_foo
    del_foo

To declare an override in a plugin, add the class field
overrides as so:
    overrides = ['set_foo', 'del_foo']

Each override must have a matching set_foo(), etc method
for implementing the new behaviour.

To enable the overrides for a particular parent stanza,
pass the option overrides=True to register_stanza_plugin.

    register_stanza_plugin(Stanza, Plugin, overrides=True)

Example code:

class Test(ElementBase):

    name = 'test'
    namespace = 'testing'
    interfaces = set(('foo', 'bar'))
    sub_interfaces = set(('bar',))

class TestOverride(ElementBase):

    name = 'test-override'
    namespace = 'testing'
    plugin_attrib = 'override'
    interfaces = set(('foo',))
    overrides = ['set_foo']

    def setup(self, xml):
        # Don't include an XML element in the parent stanza
        # since we're adding just an attribute.
        # If adding a regular subelement, no need to do this.
        self.xml = ET.Element('')

    def set_foo(self, value):
        print("overrides!")
        self.parent()._set_attr('foo', 'override-%s' % value)

register_stanza_plugin(Test, TestOverride, overrides=True)

Example usage:
>>> t = TestStanza()
>>> t['foo'] = 'bar'
>>> t['foo']
'override-bar'
2011-03-24 12:25:17 -04:00
Lance Stout
84e2589f22 Left too much unlrelated code in example. 2011-03-24 09:42:54 -04:00
Lance Stout
bf2f2782b7 Merge branch 'develop' into stream_features 2011-03-24 09:36:35 -04:00
Lance Stout
d9be51b2ef Merge branch 'develop' into roster 2011-03-24 09:36:26 -04:00
Lance Stout
a3d111be12 Added new XEP-0050 implementation.
Backward incompatibility alert!

Please see examples/adhoc_provider.py for how to use the new
plugin implementation, or the test examples in the files
tests/test_stream_xep_0050.py and tests/test_stanza_xep_0050.py.

Major changes:
    - May now have zero-step commands. Useful if a command is
      intended to be a dynamic status report that doesn't
      require any user input.
    - May use payloads other than data forms, such as a
      completely custom stanza type.
    - May include multiple payload items, such as multiple
      data forms, or a form and a custom stanza type.
    - Includes a command user API for calling adhoc commands
      on remote agents and managing the workflow.
    - Added support for note elements.

Todo:
    - Add prev action support.

You may use register_plugin('old_0050') to continue using the
previous XEP-0050 implementation.
2011-03-24 09:35:36 -04:00
Lance Stout
4916a12b6f Tidy up the examples. 2011-03-23 22:59:21 -04:00
Lance Stout
d6f2e51b05 Allow SleekTest to wait longer when checking for sent stanzas.
Now timeouts at 0.5sec instead of 0.1sec, which should prevent test
failures from stanzas being delayed longer than usual.
2011-03-23 20:23:49 -04:00
Lance Stout
feb7f892ea Fix typo. 2011-03-23 19:00:20 -04:00
Lance Stout
a420771665 Updated todo file again.
Only 11 plugins left to tidy before 1.0!
2011-03-23 10:10:54 -04:00
Lance Stout
694673b9bd Merge branch 'develop' into stream_features 2011-03-23 10:01:21 -04:00
Lance Stout
393259c24b Merge branch 'develop' into roster 2011-03-23 10:01:10 -04:00
Lance Stout
f2449009d1 Updated todo file. 2011-03-23 10:00:48 -04:00
Lance Stout
833f95b53a Cleaned XEP-0249 plugin, added tests. 2011-03-23 10:00:32 -04:00
Lance Stout
306bdd8021 Merge branch 'develop' into stream_features 2011-03-22 20:48:28 -04:00
Lance Stout
756c4c032f Merge branch 'develop' into roster 2011-03-22 20:48:09 -04:00
Lance Stout
4b1fadde4b Updated XEP-0128 plugin to work with the new XEP-0030 plugin.
Required fixing a few bugs in StanzaBase related to iterable
substanzas.
2011-03-22 20:42:43 -04:00
Lance Stout
a9685a00b3 Merge branch 'develop' into stream_features
Conflicts:
	sleekxmpp/clientxmpp.py
2011-03-22 12:08:27 -04:00
Lance Stout
e1360ae049 Merge branch 'develop' into roster 2011-03-22 12:00:01 -04:00
Lance Stout
86a6b40fd8 Updated doc for connect() 2011-03-22 11:59:27 -04:00
Lance Stout
7ef6abb2a3 May pass use_tls=False to connect().
Will disable the use of TLS for the session.
2011-03-22 11:56:55 -04:00
Lance Stout
af45b51f4f Fix error with session feature. 2011-03-18 17:57:49 -04:00
Lance Stout
566ec8a5f9 Merge branch 'develop' into stream_features
Conflicts:
	sleekxmpp/xmlstream/stanzabase.py
2011-03-18 17:39:43 -04:00
Lance Stout
b048f8d733 Merge branch 'develop' into roster 2011-03-18 17:36:35 -04:00
Lance Stout
dbf6780345 Change namespace inclusion in strings.
ElementBase instances will display the top-most namespace by default.

StanzaBase instances will NOT display the top-most namespace by default.

May pass True or False to __str__ to override.
2011-03-18 17:34:07 -04:00
Lance Stout
450c313340 Fix error in stanza handler registration in XEP-0092. 2011-03-18 17:30:29 -04:00
Lance Stout
dca8516cec Merge branch 'stream_features' of github.com:fritzy/SleekXMPP into stream_features 2011-03-18 15:52:13 -04:00
Lance Stout
f2c99798a6 Merge branch 'develop' into stream_features 2011-03-18 15:51:44 -04:00
Lance Stout
f65f88325b Merge branch 'develop' into roster 2011-03-18 15:51:27 -04:00
Lance Stout
996ca52471 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-03-18 15:48:38 -04:00
Lance Stout
6244857746 Fix self.disconnect(reconnect=True) not working. 2011-03-18 15:47:21 -04:00
Florent Le Coz
5635265203 Avoid infinite loop on version result
We need to check if type="get". otherwise we will send our version
when we will receive the version of the remote entity, and thus
going in an infinite loop.
2011-03-16 06:45:06 +08:00
Lance Stout
42c8f6ae87 Merge branch 'develop' into roster 2011-02-24 16:19:45 -05:00
Lance Stout
c2161ca56b Merge branch 'develop' into stream_features 2011-02-24 16:19:35 -05:00
Lance Stout
45ccb31356 Remove the occasional warning about XEP-0059 not loaded. 2011-02-24 16:13:44 -05:00
Lance Stout
1a81b2f464 Add tests for XEP-0085, fix some bugs. 2011-02-24 14:15:02 -05:00
Lance Stout
77251452c1 Updated the XEP-0085 plugin.
Can now be used as so:

>>> msg['chat_state']
''
>>> msg
<message />

>>> msg['chat_state'] = 'paused'
>>> msg
<message>
  <paused xmlns="http://jabber.org/protocol/chatstates" />
</message>

>>> msg['chat_state']
'paused'

>>> del msg['chat_state']
>>> msg
<message />
2011-02-24 12:10:29 -05:00
Lance Stout
4df3aa569b Bring back the signal handlers (and the "killed" event).
Now done more responsibly, saving any existing signal handlers
and calling them when an interrupt occurs in addition to the
one Sleek installs.

NOTE: You may need to explicitly use "kill <process id>" in
order to trigger the proper signal handler execution, and
to raise the "killed" event.
2011-02-23 10:20:04 -05:00
Nathan Fritz
2e2e16e281 fixes to ping: auto-ping off by default, fixed ping-time of zero bug, fixed class name mismatch 2011-02-15 15:24:58 -08:00
Lance Stout
d5b3a52827 Merge branch 'develop' into stream_features
Conflicts:
	sleekxmpp/xmlstream/stanzabase.py
2011-02-14 16:26:23 -05:00
Lance Stout
e4f3b777f9 Reset the roster on disconnect instead of replacing it. 2011-02-14 16:24:49 -05:00
Lance Stout
a278f79bdb Merge branch 'develop' into roster
Conflicts:
	sleekxmpp/clientxmpp.py
2011-02-14 16:18:44 -05:00
Lance Stout
d709f8db65 Use the _build_stanza method. 2011-02-14 13:50:59 -05:00
Lance Stout
75584d7ad7 Remap old method names in a better way.
This should prevent some reference cycles that will cause garbage
collection issues.
2011-02-14 13:49:43 -05:00
Lance Stout
e0f9025e7c More attempts at fixing garbage collection.
Don't keep a reference to stanzas in Callback objects.
2011-02-14 11:30:04 -05:00
Lance Stout
9004e8bbf2 Break references that can prevent garbage collection. 2011-02-14 11:13:41 -05:00
Lance Stout
8b5511c7ec Simplification when removing a deletable handler. 2011-02-13 16:40:04 -05:00
Lance Stout
34f6195ca5 Return the name of the registered callback.
Instead of the actual callback object, return just the name of
the callback object created when using iq.send(callback=..).

This will help prevent memory leaks by not keeping an additional
reference to the object, but still allows for the callback to be
canceled by using self.remove_handler("handler_name").
2011-02-13 16:30:57 -05:00
Lance Stout
70af52d74c Make one-off Callbacks ready for deletion after the prerun step.
Waiting until the actual run step means that the handler is not
marked for deletion when checked in the __spawn_event() thread,
causing the callback to stay in the handler list.
2011-02-13 16:28:06 -05:00
Lance Stout
ca2b4a188a Return the registered callback when using iq.send(callback=foo).
Allows for a callback to be canceled by unregistering the
returned handler.
2011-02-12 11:01:43 -05:00
Lance Stout
0d32638379 XMPPError exceptions can keep a stanza's contents.
This allows exceptions to include the original
content of a stanza in the error response by including
the parameter clear=False when raising the exception.
2011-02-11 15:20:26 -05:00
Lance Stout
c4b1212c44 Updated XEP-0199 plugin.
Now has docs and uses the new plugin format.
2011-02-11 00:30:45 -05:00
Nathan Fritz
3463bf46c6 added option to return false on ping error, added ping example 2011-02-10 13:45:35 -08:00
Lance Stout
13a01beb07 Fix same error for get_info default behaviour. 2011-02-09 09:12:44 -05:00
Lance Stout
145f577bde Fix get_items default behaviour. 2011-02-09 08:58:00 -05:00
Lance Stout
30da68f47b Update XEP-0060 test. 2011-02-08 21:09:49 -05:00
Florent Le Coz
72ead3d598 Replace the print statement by a log.debug call
This print syntax is deprecated in python3, so
the plugin was working only with python2
2011-02-09 10:02:14 +08:00
Florent Le Coz
4b71fba64c Fix the xep_0009 import (no more relatives)
Also, remove trailing spaces in all files
of this plugin
2011-02-09 10:02:14 +08:00
Lance Stout
606c369173 Some more roster tweaks. 2011-02-08 19:15:50 -05:00
Stefan de Konink
1ed06bebcd This fixes the configuration stuff, because type is form not submit with setNodeConfiguration. 2011-02-07 23:55:46 +08:00
Lance Stout
aa1996eba6 Fixed failing tests from new XEP-0009 plugin 2011-02-07 10:18:15 -05:00
Nathan Fritz
683f717cf7 fixed merge 2011-02-05 04:54:52 -08:00
Lance Stout
3c871920b1 Make the roster backend settable. 2011-02-02 12:05:34 -05:00
Lance Stout
de6170a13d Merge branch 'develop' into roster
Conflicts:
	sleekxmpp/basexmpp.py
2011-02-02 09:13:22 -05:00
Lance Stout
8dbe6f6546 Updated todo list for 1.0 release. 2011-01-31 15:54:44 -05:00
Lance Stout
5313338c3a Fixes for XEP-0202 2011-01-31 15:40:00 -05:00
Lance Stout
1a270dc05c First pass at re-worked stream features.
Stream features now use stanza objects!

Features are given a ranking that expresses the dependency
relationships (since only one feature is negotiated at a time, the
dependency graph can be replaced by a line).

>>> xmpp.register_feature('my_feature', _my_handler,
>>>                       restart=True,   # Requires stream restart
>>>                       order=600)      # Ranking (out of ~ 10,000,
>>>                                       # lower #'s executed first)

SASL mechanisms may now be added or disabled as needed. Each mechanism
is given a priority value indicating the order in which the client
wishes for mechanisms to be tried. Higher priority numbers are executed
first.

>>> xmpp.register_sasl_mechanism('SASL-MECH', _mech_handler,
>>>                              priority=0)

Disabling a SASL mechanism:

>>> xmpp.remove_sasl_mechanism('ANONYMOUS')
2011-01-28 00:49:37 -05:00
Lance Stout
bd9bf3f1c7 Update tostring methods.
Will now always show top-level namespace, unless it is the same
as the stream's default namespace. Also added the XMPP stream
namespace to the namespace map as 'stream'.
2011-01-27 18:05:05 -05:00
Lance Stout
cd800d636a Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-01-27 16:05:15 -05:00
Lance Stout
40642b2cd1 Make StreamError work properly.
Now uses the correct namespaces and condition names.
2011-01-27 16:02:57 -05:00
Lance Stout
35ef8f9090 Make stanza.plugins an OrderedDict.
This allows you to determine the order in which substanzas
were added in the original XML.
2011-01-27 16:01:35 -05:00
Lance Stout
38dc35840e Recognize stanzas that don't use the default namespace. 2011-01-27 15:59:50 -05:00
Florent Le Coz
b4004cd4d6 xep_0045: fix the 'to' value when configuring room 2011-01-27 09:34:32 +08:00
Lance Stout
0c8a8314b2 Cleanup for stanzabase.
Use stanza.values instead of _get/set_stanza_values where used.

ElementBase stanzas can now use .tag

May use class method tag_name() for stanza classes.

ElementBase now has .clear() method.
2011-01-26 11:27:41 -05:00
Lance Stout
4e757c2b56 Upgraded how subitem works.
May now use register_stanza_plugin(Foo, Bar, iterable=True)
to add to the set of stanza classes used for iterable
substanzas. It is no longer necessary to manually specify
the contents of subitem if the new method is used.
2011-01-26 10:04:36 -05:00
Stefan de Konink
c3be6ea0b2 My hunch is that these should also be updated. 2011-01-23 02:08:29 +08:00
Lance Stout
da332365d4 Make extending stanza objects nicer.
A stanza object may add is_extension = True to its class definition
to provide a single new interface to a parent stanza.

For example:

import sleekxmpp
from sleekxmpp import Iq
from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin, ET

class Foo(ElementBase):
    """
    Test adding just an attribute to a parent stanza.

    Adding subelements works as expected.
    """
    is_extension = True
    interfaces = set(('foo',))
    plugin_attrib = 'foo'

    def setup(self, xml):
        # Don't include an XML element in the parent stanza
        # since we're adding just an attribute.
        # If adding a regular subelement, no need to do this.
        self.xml = ET.Element('')

    def set_foo(self, val):
        self.parent()._set_attr('foo', val)

    def get_foo(self):
        return self.parent()._get_attr('foo')

    def del_foo(self):
        self.parent()._del_attr('foo')

register_stanza_plugin(Iq, Foo)

i1 = Iq()
i2 = Iq(xml=ET.fromstring("<iq xmlns='jabber:client' foo='bar' />"))

>>> i1['foo'] = '3'
>>> i1
'3'
>>> i1
'<iq id="0" foo="3" />'
>>> i2
'<iq id="0" foo="bar" />'
>>> i2['foo']
'bar'
>>> del i2['foo']
>>> i2
'<iq id="0" />'
2011-01-19 19:49:13 -05:00
Lance Stout
f7e7bf601e Fix tests for Nick stanza. 2011-01-19 19:03:02 -05:00
Lance Stout
6f4c2f22f3 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-01-19 17:50:05 -05:00
Lance Stout
493df57035 Fix thirdparty imports for Python3 2011-01-19 17:49:39 -05:00
Florent Le Coz
897a9ac333 Do not traceback when DNS resolution time out.
Just log that the resolution timed out, and fall back
to the hostname from the JID in this case
2011-01-20 06:34:08 +08:00
Lance Stout
acc2d071ac Fix disco add_item.
If no JID is specified for the item, use xmpp.boundjid.full.
2011-01-19 17:27:53 -05:00
Lance Stout
d3b1f8c476 Fix namespace for Nick stanza. 2011-01-19 16:47:18 -05:00
Lance Stout
f1db2fc156 Fix error in disco add_item.
None values were not being treated properly.
2011-01-19 12:08:28 -05:00
Lance Stout
2004ddd678 Add StreamError stanza and a stream_error event.
Note that the stream may automatically attempt to
reconnect when a stream error is received.
2011-01-16 13:22:52 -05:00
Lance Stout
cb85d4a529 Raise the event 'socket_error' when a socket error occurs.
Will be most useful for debugging and responding to failed
connection attempts.
2011-01-16 13:07:39 -05:00
Lance Stout
ead3af3135 Make it easier to import OrderedDict 2011-01-15 17:15:33 -05:00
Lance Stout
a2891d7608 Fix how disco plugin looks up info and items for clients. 2011-01-15 10:08:35 -05:00
Lance Stout
d7dea0c6cc Add a note for debug statement when running scheduled events.
Fixes the intermittent DEBUG ((),) messages that give no
explanation.

Will now show as:
DEBUG Scheduled event: ((), )
2011-01-14 12:07:25 -05:00
Lance Stout
65931bb384 Merge branch 'develop' into roster 2011-01-13 22:16:46 -05:00
Lance Stout
632827f213 Fix bug in JID class. Attribute .jid now works properly. 2011-01-13 10:21:20 -05:00
Lance Stout
b71550cec7 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2011-01-13 10:20:34 -05:00
Dann Martens
b68e7bed40 Fixed typo. 2011-01-13 15:04:16 +01:00
Dann Martens
4be6482ff3 Fixed 'nil' bug in unmarshalling. 2011-01-13 13:42:01 +01:00
Dann Martens
a21178007f Updated setup.py to include XEP-0009. 2011-01-13 12:53:17 +01:00
Dann Martens
2e6c27f665 Added examples. 2011-01-13 11:58:20 +01:00
Dann Martens
0a3a7b5a70 Removed binding XML namespace experiments. 2011-01-13 11:37:58 +01:00
Dann Martens
3a12cdbd13 Introduced new XEP-0009 into develop. 2011-01-13 08:40:53 +01:00
Lance Stout
7d93d1824b Fix setup.py and old_0004.py typo bugs. 2011-01-12 12:22:48 -05:00
Lance Stout
ba0d699d83 Fix ordering error in Iq._set_stanza_values.
If iq['query'] was set before a plugin that used the query
element was set, then the query element was duplicated.
2011-01-12 08:55:33 -05:00
Lance Stout
c6ac40c476 Update setup.py with latest plugin packages. 2011-01-11 11:30:56 -05:00
Lance Stout
a5d53b3349 Fix method call error. 2011-01-10 16:33:30 -05:00
Lance Stout
4487a90623 Add self.client_roster to make things simpler for clients. 2011-01-10 16:28:49 -05:00
Lance Stout
05da8cc3d1 Merge branch 'develop' into roster 2011-01-10 16:27:18 -05:00
Te-je Rodgers
fe3f8dde4b added plugin for xep-0249 2011-01-11 04:11:05 +08:00
Lance Stout
23e499998f Merge branch 'develop' into roster 2011-01-09 10:04:09 -05:00
Lance Stout
acdf9e2d22 Need to run post_init properly. 2011-01-09 10:03:32 -05:00
Lance Stout
2076d506b4 Update the XEP-0092 plugin to the new style. 2011-01-08 22:38:13 -05:00
Florent Le Coz
68ce47c905 Allow XEP 0092 to send os information
Doesn't send these information by default, only if provided in the
config dict (as the 'os' key)
2011-01-09 10:08:44 +08:00
Lance Stout
7c7fa0f008 Add support for XEP-0059 to XEP-0030 plugin. 2011-01-08 11:19:31 -05:00
Lance Stout
a8e3657487 Added new XEP-0059 plugin.
Contributed by Erik Reuterborg Larsson (who).
2011-01-08 10:58:47 -05:00
Lance Stout
13a2f719f4 Add reattempt to ClientXMPP.connect 2011-01-07 16:41:31 -05:00
Lance Stout
2908751020 Allow JID objects to be compared with strings.
Two JIDs match if they have the same full JID value.
2011-01-05 20:16:15 -05:00
Lance Stout
c156a4f723 Merge branch 'develop' into roster 2011-01-05 16:53:33 -05:00
Lance Stout
8b29431cde More clarification in docs for XEP-0030 plugin. 2011-01-04 19:39:10 -05:00
Lance Stout
4b145958fa Clarify docs for disco.get_info. 2011-01-04 18:38:21 -05:00
Lance Stout
e08b0054b2 Keep things lined up. 2010-12-29 15:01:50 -05:00
Andrzej Bieniek
596e135a03 Fixed typo in comment. 2010-12-28 21:32:28 +00:00
Lance Stout
e55e213c78 Fix some typos. 2010-12-28 16:17:08 -05:00
Lance Stout
8749f5e09b Make the new XEP-30 plugin retain older API signatures. 2010-12-28 15:43:00 -05:00
Lance Stout
b3353183f3 Added ordereddict implementation to thirdparty.
See http://pypi.python.org/pypi/ordereddict and
http://code.activestate.com/recipes/576693/.
2010-12-21 17:33:31 -05:00
Lance Stout
3657bf6636 Merge branch 'develop' into roster 2010-12-21 11:33:40 -05:00
Lance Stout
f97f6e5985 More documentation for XEP-0030 plugin. 2010-12-21 11:33:03 -05:00
Lance Stout
34c374a1e1 Make tests pass for catching exceptions.
May now use sys.excepthook to catch exceptions
from threaded handlers.
2010-12-17 13:11:03 -05:00
Lance Stout
506eccf84d Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2010-12-17 10:44:32 -05:00
Florent Le Coz
982bf3b2ec RootStanza raises unexpected exceptions
We now raise the unexpected exceptions instead of sending
them on the network.
 - avoids flood (sending a traceback on a MUC, for example…) and
   maybe some security issues.
 - lets you handle the traceback (catch it to handle
   it properly, or with except_hook, etc)
 - an exception cannot be raised without you knowing
2010-12-17 23:43:48 +08:00
Lance Stout
53a5026301 Almost done with xep-30; added more docs. 2010-12-16 23:52:17 -05:00
Lance Stout
0aee445e69 Use daemon threads instead of signals.
Daemonized threads exit once the main program has quit,
and the only threads left running are all daemon threads.

Should fix hanging clients while not trampling over anyone
else's signal handlers.
2010-12-16 22:21:50 -05:00
Lance Stout
adade2e5ec Merge branch 'develop' into roster 2010-12-16 22:03:56 -05:00
Lance Stout
cbc42c29fb Updated echo_client example to mention SSL options. 2010-12-16 22:00:20 -05:00
Lance Stout
874c51d74d Added the disco browser as an example. 2010-12-16 21:58:53 -05:00
Lance Stout
f9ac95ddb7 Need to update setup.py with new XEP-0030 packages.
Will need to remember to update setup.py when transitioning
plugins to the new layout.
2010-12-16 21:15:13 -05:00
Lance Stout
0ea014fe41 Updated the list of plugins in sleekxmpp.plugins.__init__ 2010-12-16 18:29:56 -05:00
Lance Stout
62b190d0ff Fixed specifying 'from' values in XEP-0045 plugin.
Methods now accept either an ifrom or mfrom parameter
to specify a 'from' value. Client connections should not
need to use these, but component connections must use them.
2010-12-16 18:14:33 -05:00
Lance Stout
4b57b8131f Added support for using SSL CA certificates.
Originally provided by Brian Beggs (macdiesel)
and Thom Nichols (tomstrummer).
2010-12-16 17:30:08 -05:00
Lance Stout
988a90a176 Added MUC invite handler to XEP-0045 plugin.
Originally contributed by damium/romeira, with some
modifications.

Also, converted tabs to spaces to prepare for future cleanup.
2010-12-16 16:18:49 -05:00
Lance Stout
67775fb8bd Use boundjid in plugins instead of the deprecated accessors.
Originally contributed by skinkie, with a few modifications.
2010-12-16 15:38:00 -05:00
Lance Stout
e81683beee Some Python 3.1+ compatibility fixes.
Originally contributed by filipegiusti.
2010-12-16 15:29:17 -05:00
Lance Stout
d9c25ee65c Added more options to the make_iq_* methods.
May include a to and from JID in make_iq_* calls.

May pass an existing iq stanza to most of them instead of generating
a new stanza.

make_iq now accepts a 'to' value, 'type' value, and 'query' value to
simplify things a bit more.
2010-12-16 15:25:04 -05:00
Lance Stout
1ebc7f4d4b Implement a few more static node handlers. 2010-12-15 19:22:21 -05:00
Lance Stout
2c5b77ae2e And some more docs. 2010-12-15 18:57:45 -05:00
Lance Stout
d8aae88526 The documentation effort continues.
Also, need to start working on a replacement for the XEP-30 page in the
wiki since the API has changed significantly.
2010-12-15 17:58:15 -05:00
Lance Stout
2f4bdfee1b Update some docs. 2010-12-13 15:58:59 -05:00
Lance Stout
c16913c999 Merge branch 'develop' into roster
Conflicts:
	sleekxmpp/basexmpp.py
2010-12-13 14:36:53 -05:00
Lance Stout
f4451fe6b7 First pass at a new XEP-0030 plugin.
Now with dynamic node handling goodness.

Some things are not quite working yet, in particular:
    set_items
    set_info
    set_identities
    set_features

And still need more unit tests to round things out.
2010-12-09 18:57:27 -05:00
Lance Stout
8d4e77aba6 Fix xml:lang tostring test. 2010-12-08 00:18:04 -05:00
Lance Stout
f474d378ef Add support for using xml:lang values.
Support is only for adding literal XML content
to stanzas. Full support for things like multiple
message bodies with different xml:lang values is
still in the works.
2010-12-07 23:07:40 -05:00
Lance Stout
defc252c7d Fix several errors in SleekTest.
Notably, not sending an expected stanza will not silently pass.
2010-12-07 23:04:37 -05:00
Lance Stout
19bd1e0485 Actually make the Iq callbacks work for real. 2010-12-07 23:04:04 -05:00
Lance Stout
5f2fc67c40 Added option for iq.send to accept a callhandler.
The callback will be a stream level handler, and will not
execute in its own thread. If you must have a thread, have the
callback function raise a custom event, which can be processed
by another event handler, which may run in an individual thread,
like so:

def handle_reply(self, iq):
    self.event('custom_event', iq)

def do_long_operation_in_thread(self, iq):
    ...

self.add_event_handler('custom_event', self.do_long_operation_in_thread)

...take out already prepared iq stanza...
iq.send(callback=self.handle_reply)
2010-12-07 17:19:39 -05:00
Lance Stout
8ead33fc3b Fixed typo 2010-11-18 16:23:18 -05:00
Lance Stout
ab25301953 Adding stream tests for XEP-0030.
Fixed some errors when responding to disco requests.
2010-11-18 15:50:45 -05:00
Lance Stout
291b118aca XEP-0030 bug fixes. 2010-11-18 11:22:11 -05:00
Lance Stout
12b61365ad Merge branch 'develop' of github.com:fritzy/SleekXMPP into roster 2010-11-18 01:18:06 -05:00
Lance Stout
db7fb10e95 Add rename_node method to disco plugin. 2010-11-18 01:15:34 -05:00
Lance Stout
9214dc6610 Add rename_node method to disco plugin. 2010-11-18 01:12:11 -05:00
Lance Stout
58b95e4ae4 Merge branch 'develop' of github.com:fritzy/SleekXMPP into roster 2010-11-18 00:44:51 -05:00
Lance Stout
60d3afe6b6 Added __repr__ for JIDs. 2010-11-18 00:03:39 -05:00
Lance Stout
afeb8f3f7c Made echo client print help message.
If the jid and password are not supplied, the options list will be
displayed instead of hanging trying to connect to a nonexistant server.
2010-11-17 17:30:53 -05:00
Lance Stout
cdbc0570ca Added a basic example for using MUC. 2010-11-17 17:28:04 -05:00
Lance Stout
e648f08bad Fix stream test errors. 2010-11-17 16:08:14 -05:00
Lance Stout
7ba6d5e02d Fix Node set to None error. 2010-11-17 16:01:27 -05:00
Lance Stout
ea48bb5ac5 Fixed some live stream test errors.
Added test demonstrating using multiple stream clients
in a single test.
2010-11-17 15:45:16 -05:00
Lance Stout
6ee8a2980c Fix RESPONSE_TIMEOUT dependency loops. 2010-11-17 15:13:09 -05:00
Lance Stout
debf909359 Merge branch 'develop' into roster 2010-11-17 14:33:09 -05:00
Lance Stout
b8114b25ed Make live stream tests work better.
SleekTest can now use matchers when checking stanzas, using
the method parameter for self.check(), self.recv(), and self.send():
    method='exact'      - Same behavior as before
           'xpath'      - Use xpath matcher
           'id'         - Use ID matcher
           'mask'       - Use XML mask matcher
           'stanzapath' - Use StanzaPath matcher

recv_feature and send_feature only accept 'exact' and 'mask' for now.
2010-11-17 13:43:15 -05:00
Lance Stout
0d4d84b2fa Cleaned basexmpp.py 2010-11-17 10:13:45 -05:00
Lance Stout
d2aca3e7e0 Remove extraneous files. 2010-11-17 02:04:36 -05:00
Lance Stout
26aca2b789 Merge branch 'roster' of github.com:fritzy/SleekXMPP into roster
Conflicts:
	sleekxmpp/basexmpp.py
	sleekxmpp/roster.py
	sleekxmpp/test/sleektest.py
	tests/test_stream_presence.py
	tests/test_stream_roster.py
2010-11-17 02:01:12 -05:00
Lance Stout
5424ede413 More cleanup. 2010-11-17 01:54:30 -05:00
Lance Stout
20112f8e16 More docs! 2010-11-17 01:49:51 -05:00
Lance Stout
4260a754e5 Added more docs. 2010-11-17 01:49:51 -05:00
Lance Stout
ce30f72738 Added docs for main Roster class. 2010-11-17 01:49:51 -05:00
Lance Stout
69d430dd75 Cleaned up names. 2010-11-17 01:49:51 -05:00
Lance Stout
673545c7e4 First pass at integrating the new roster manager. 2010-11-17 01:49:19 -05:00
Nathan Fritz
45991e47ee scheduler no longer waits for the next event before exiting 2010-11-16 17:58:20 -08:00
Nathan Fritz
b8f40eb843 xep_0199 ping now uses scheduler instead of dedicated thread 2010-11-16 17:43:05 -08:00
Florent Le Coz
b73a859031 Add a groupchat_subject event
Use this event to get notified of the subject changes (or to get the
subject of the room when joining one)
2010-11-10 05:54:22 +08:00
Florent Le Coz
9dbf246f0b Doesn't fail if host has NO SRV record
Just catch an other exception type coming from the dns resolver that
could be raised with hosts like "anon.example.com" which just don't have
any SRV record.
2010-11-09 01:53:41 +08:00
Lance Stout
4fb77ac878 Logging no longer uses root logger.
Each module should now log into its own logger.
2010-11-06 01:28:59 -04:00
Lance Stout
d0c506f930 Simplified SleekTest.
* check_stanza does not require stanza_class parameter. Introspection!
* check_message, check_iq, and check_presence removed -- use check
  instead.
* stream_send_stanza, stream_send_message, stream_send_iq, and
  stream_send_presence removed -- use send instead.
* Use recv instead of recv_message, recv_presence, etc.
* check_jid instead of check_JID
* stream_start may accept multi=True to return a new SleekTest instance
  for testing multiple streams at once.
2010-11-05 21:18:48 -04:00
Lance Stout
7351fe1a02 Fix bug introduced while fixing another bug.
Threaded event handlers now handle exceptions again.
2010-11-04 14:35:35 -04:00
Nathan Fritz
38c2f51f83 fixed indent errors 2010-11-04 11:39:41 -07:00
Lance Stout
1bf34caa5b Fixes for XEP-0199 plugin.
Quick fixes to get the XEP-0199 plugin working until a proper cleanup is
done.
2010-11-03 14:04:18 -04:00
Lance Stout
5769935720 Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2010-11-03 12:39:44 -04:00
Lance Stout
0214db7545 Catch exceptions for direct events.
Events triggered with direct=True will have exceptions caught.

Note that all event handlers in a direct event will currently run
in the same thread.
2010-11-03 12:38:13 -04:00
Lance Stout
ffc6f031d9 Updated namespaced used in the XEP-0199 plugin. 2010-11-03 12:37:26 -04:00
Lance Stout
9e248bb852 Fix bug in XEP-0030 plugin.
xep_0030 still referenced event_handlers. Added the method event_handled
which will return the number of registered handlers for an event to
resolve the issue.
2010-10-31 18:27:52 -04:00
Lance Stout
973890e2c9 Added try/except for setting signal handlers.
Setting signal handlers from inside a thread is not supported in Python,
but some applications need to run Sleek from a child thread.

SleekXMPP applications that run inside a child thread will NOT be able
to detect SIGHUP or SIGTERM events. Those must be caught and managed by
the main program.
2010-10-28 10:42:23 -04:00
Lance Stout
9c08e56ed0 SSL and signal fixes.
Made setting the SIG* handlers conditional on if the signal defined for
the OS.

Added the attribute ssl_version to XMLStream to set the version of SSL
used during connection. It defaults to ssl.PROTOCOL_TLSv1, but OpenFire
tends to require ssl.PROTOCOL_SSLv23.
2010-10-27 19:27:47 -04:00
Lance Stout
4f69a03bb1 More cleanup. 2010-10-27 15:02:21 -04:00
Lance Stout
c2c18acd6a More docs! 2010-10-27 13:36:23 -04:00
Lance Stout
12ead36f96 Added more docs. 2010-10-27 10:51:58 -04:00
Lance Stout
41a642e06c Added docs for main Roster class. 2010-10-27 09:27:00 -04:00
Lance Stout
c6ed4b8a1d Cleaned up names. 2010-10-27 08:09:50 -04:00
Lance Stout
65aa6573df First pass at integrating the new roster manager. 2010-10-26 23:47:17 -04:00
Lance Stout
b888610525 Added XEP-202 Entity Time plugin.
Contributed by Cesar Alcalde.
2010-10-25 21:26:25 -04:00
Lance Stout
6d68706326 Added XEP-0012 Last Activity plugin.
Contributed by Cesar Alcalde.
2010-10-25 20:37:02 -04:00
Lance Stout
5bdcd9ef9d Made exceptions work.
Raising an XMPPError exception from an event handler now works, even if
from a threaded handler.

Added stream tests to verify.

We should start using XMPPError, it really makes things simple!
2010-10-25 15:09:56 -04:00
Lance Stout
2eff35cc7a Added more presence stream tests.
Tests auto_authorize=False, and got_online.
2010-10-25 13:21:00 -04:00
Lance Stout
ac330b5c6c Fixed bug in presence subscription handling.
Subscription requests and responses were not setting the correct 'to'
attribute.
2010-10-25 12:52:32 -04:00
Lance Stout
46ffa8e9fe Added stream tests for presence events.
First batch of tests, currently focuses on the got_offline event.
2010-10-24 19:57:07 -04:00
Lance Stout
03847497cc Added test for error stanzas. 2010-10-24 19:56:42 -04:00
Lance Stout
185d7cf28e More JID unit tests.
sleekxmpp.xmlstream.jid now has 100% coverage!
2010-10-24 19:06:54 -04:00
Lance Stout
8aa3d0c047 Fixed got_offline triggering bug. 2010-10-24 18:56:50 -04:00
Lance Stout
9e3d506651 Fixed resource bug in JIDs.
JIDs without resources will return '' instead of the bare JID.

Cleaned up JID tests, and added check_JID to SleekTest.
2010-10-24 18:22:41 -04:00
Lance Stout
2f3ff37a24 Make SleekTest streams register all plugins.
Makes test coverage nicer.
2010-10-24 17:35:11 -04:00
Lance Stout
1f09d60a52 ComponentXMPP saves all of its config data now.
ComponentXMPP was ignoring plugin_config and plugin_whitelist
parameters, making register_plugins() fail.
2010-10-24 17:33:11 -04:00
Lance Stout
d528884723 Added stream tests for rosters. 2010-10-24 12:53:14 -04:00
Lance Stout
d9aff3d36f Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2010-10-24 12:11:34 -04:00
Lance Stout
04cc48775d Fixed error in client roster handling.
The roster result iq was not being passed to the roster update
handler.
2010-10-24 12:08:59 -04:00
Nathan Fritz
27ebb6e8f6 presence no longer replies when exception is caught and tweaks to presence events 2010-10-21 16:59:15 -07:00
Lance Stout
8f55704928 Fixed mixed text and elements bug in tostring.
XML of the form <a>foo <b>bar</b> baz</a> was outputted as
<a>foo <b>bar</b> baz baz</a>.

Includes unit test.
2010-10-21 16:21:28 -04:00
Nathan Fritz
d88999691c misc small tweaks 2010-10-20 20:14:26 -07:00
Nathan Fritz
c4699b92e6 pep8 fixes on core library 2010-10-20 19:43:53 -07:00
Nathan Fritz
ce69213a1e when disconnected, reset the roster 2010-10-20 19:33:40 -07:00
Nathan Fritz
77eab6544f reconnect if session isn't established within 15 seconds 2010-10-20 19:18:27 -07:00
Nathan Fritz
11264fe0a8 capture SIGHUP and SIGTERM (windows) and disconnect; also testall no longer loads string26 with python3 2010-10-20 17:30:12 -07:00
Nathan Fritz
11a6e6d2e0 fixed logic error in state machine 2010-10-20 16:57:47 -07:00
Nathan Fritz
6e34b2cfdd fixed disconnect 2010-10-20 16:32:50 -07:00
Lance Stout
e18354ae0e Continue converting to underscored names. 2010-10-18 09:06:54 -04:00
Lance Stout
4375ac7d8b Underscore names by default.
Stanza objects now accept the use of underscored names.

The CamelCase versions are still available for backwards compatibility,
but are discouraged.

The property stanza.values now maps to the old getStanzaValues and
setStanzaValues, in addition to _set_stanza_values and
_get_stanza_values.
2010-10-17 22:04:42 -04:00
Lance Stout
faec86b3be Import plugins from string referenced modules. 2010-10-17 15:47:24 -04:00
Lance Stout
505a63da3a Cleanup, restore PEP8. 2010-10-16 21:15:31 -04:00
Florent Le Coz
93fbcad277 Fix the error on non-number priority
The priority is not a number: we consider it 0 as a default
2010-10-17 09:01:53 +08:00
Florent Le Coz
3625573c7d Default history is 0 2010-10-17 09:01:53 +08:00
Florent Le Coz
d9e7f555e6 MUC leave message and MUC history request
It is now possible to ask for "any number of history stanzas" when
joining a muc (with history=None).
Also we use "maxchars" when asking NO history ("0") since it's a MUST in
the XEP.
And you can specify a message when leaving a MUC.
2010-10-17 09:01:52 +08:00
Florent Le Coz
2755d732a4 Remove deprecation warnings
Remove all the deprecation warnings by using only boundjid.
And also fix a indentation error.
2010-10-17 08:55:30 +08:00
Florent Le Coz
2d18d905a5 Anonymous authentication
Implemented ANONYMOUS authentication on the ClientXMPP class.
To use it, you just need to provide a domain (e.g 'anon.example.com')
with an optional resource (e.g 'anon.example.com/resource') as the JID,
with no password. The JID class has been improved to accept
domains as fulljid.

You can test this with echo_client.py
python echo_client.py -j anon.louiz.org/  # anonymous with a resource
                                          # defined by the server
python echo_client.py -j anon.louiz.org/resource  # anonymous with given
                                                  # resource

The "normal" authentication method still works exactly like before.
2010-10-17 08:55:30 +08:00
Lance Stout
4eb4d729ee Fixed setup.py to use py_modules in the setup call. 2010-10-16 20:48:51 -04:00
Nathan Fritz
8b5c1010de fixed JID to accept server/domain/host as the same 2010-10-14 16:34:16 -07:00
Nathan Fritz
95ad8a1878 fixed stream test not disconnecting cleanly 2010-10-14 16:27:44 -07:00
Nathan Fritz
aeb7999e6a don't import statemachine 2010-10-14 16:08:50 -07:00
Nathan Fritz
8468332381 fixed stream tests 2010-10-14 15:53:10 -07:00
Nathan Fritz
dc001bb201 deprecated jid, fulljid, server, user, resource properties and added boundjid JID 2010-10-14 15:50:54 -07:00
Nathan Fritz
0d0b963fe5 fixed socket name collision in xmlstream.py and fixed python 3.x compatibility 2010-10-14 10:58:07 -07:00
Nathan Fritz
a41a4369c6 disconnect cleanly 2010-10-13 18:21:05 -07:00
Nathan Fritz
7ad7a29a8f new state machine in place 2010-10-13 18:15:21 -07:00
Lance Stout
b0e036d03c Added example live stream test.
Run using:
python tests/live_test.py
2010-10-07 19:46:40 -04:00
Lance Stout
a8b948cd33 SleekTest may now run against a live stream.
Moved SleekTest to sleekxmpp.test package.
Corrected error in XML compare method.
Added TestLiveSocket to run stream tests against live streams.
Modified XMLStream to work with TestLiveSocket.
2010-10-07 19:43:51 -04:00
Lance Stout
e02ffe8547 Corrected test errors.
There was a bug in the XML compare method.
2010-10-07 19:42:28 -04:00
Lance Stout
42bfca1c87 Removed debug log statement. 2010-10-07 19:41:33 -04:00
Lance Stout
0fffbb8200 Unit test reorganization.
Moved SleekTest to sleekxmpp.test.

Organized test suites by their focus.
- Suites focused on testing stanza objects are named test_stanza_X.py
- Suites focused on testing stream behavior are name test_stream_X.py
2010-10-07 10:58:13 -04:00
Lance Stout
21c32c6e1c Moved the pubsub tester to conn_tests. 2010-10-07 10:28:38 -04:00
Lance Stout
75a051556f Changed SleekTest to use underscored names. 2010-10-07 09:22:27 -04:00
Lance Stout
78141fe5f3 Fixed dealing with deleting handlers.
The call to .index() may raise a ValueError if the item is not in the
list. So both the .index() and .pop() calls should be in the try block.
2010-10-07 09:17:28 -04:00
Lance Stout
88d21d210c Corrected stream header tester.
Added test for testing stream headers.
2010-10-06 18:46:23 -04:00
Lance Stout
799645f13f Updated method names.
Using underscored names where possible.
2010-10-06 18:45:11 -04:00
Lance Stout
f234dc02cf Updated SleekTest and related tests.
May now use a component for stream testing.
Methods provided for testing stream headers.
2010-10-06 18:10:04 -04:00
Lance Stout
c294c1a85c More PEP8 compliance cleanups.
Cleaned up the atom entry stanza.
2010-10-06 15:12:39 -04:00
Lance Stout
cbe76c8a70 Cleaned up the Scheduler. 2010-10-06 15:03:21 -04:00
Lance Stout
77b8f0f4bb Fixed whitespace issue. 2010-10-06 14:31:33 -04:00
Lance Stout
259f91d2bd Updated todo list. 2010-10-06 14:23:46 -04:00
Lance Stout
ed366b338d Moved ClientXMPP to clientxmpp.py.
Cleaned up the __init__.py files.
2010-10-06 14:20:32 -04:00
Lance Stout
9e2cada19e Missed a few docstrings. 2010-10-06 14:09:14 -04:00
Lance Stout
d0ccbf6b7a Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2010-10-06 14:06:02 -04:00
Lance Stout
e1866ab328 Made first pass at cleaning up ClientXMPP.
Added self.stream_ns to BaseXMPP.
Moved connected/disconnected events and logging to XMLStream.
2010-10-06 14:03:19 -04:00
fritzy
3ffa09ba7c deal with deleting handlers that are no longer there 2010-10-06 17:58:03 +00:00
Lance Stout
a7410f2146 Made a first pass at cleaning up ComponentXMPP. 2010-10-06 10:47:05 -04:00
Lance Stout
178608f4c0 XMLStream cleanup.
Added RestartStream as a top level item in sleekxmpp.xmlstream.

Fixed trailing whitespace.
2010-10-06 10:45:36 -04:00
Lance Stout
19ee6979a5 Updated 1.0 release todo list. 2010-10-03 22:31:22 -04:00
Lance Stout
9f0baec7b2 Made first pass at cleaning BaseXMPP.
Have not intregrated the new JID class yet.
2010-10-01 23:56:46 -04:00
Lance Stout
433c147627 Fixed typo in XEP-0033 plugin. 2010-10-01 21:25:27 -04:00
Lance Stout
9a34c9a9a1 Modified event handling to use the event queue.
Updated tests to match. (Needed to add a small wait to make sure
the event got through the queue before checking the results.)
2010-10-01 13:49:58 -04:00
Lance Stout
2662131124 Fixed tostring bug when using mapped namespaces. 2010-10-01 12:41:35 -04:00
Lance Stout
bb219595a7 Moved event functions to XMLStream.
This is just a transplant, modifying event to use the main
event queue has not been implemented yet.
2010-10-01 12:24:49 -04:00
Lance Stout
fcdd57ce54 Moved add_handler, send, and sendXML to XMLStream. 2010-10-01 11:15:51 -04:00
Lance Stout
5522443e0e Moved getNewId and getId to XMLStream.
This prepares the way for moving add_handler to XMLStream.

Since stanzas, matchers, and handlers in any XML stream will typically
use unique IDs, XMLStream is a good place for these methods.
2010-10-01 10:46:37 -04:00
Lance Stout
55cfe69fef Cleaned up trailing whitespace. 2010-10-01 10:09:10 -04:00
Lance Stout
6de87a1cbf Fixed line lengths and trailing whitespace.
The pep8 command is now pleased.
2010-09-30 13:06:16 -04:00
Lance Stout
7c10ff16fb Made a first pass at cleaning up XMLStream.
A few extra methods are mentioned in the docs, but those have not
been moved to XMLStream from BaseXMPP yet.
2010-09-30 12:56:22 -04:00
Nathan Fritz
c258d2f19d added room events for specific rooms, added buildForm to xep_0004 plugin 2010-09-23 00:51:23 +00:00
fritzy
d576e32f7a Merge branch 'develop' of git@github.com:fritzy/SleekXMPP into develop 2010-09-02 20:01:28 +00:00
Lance Stout
4a2e7c5393 Fixed linespacing and whitespace issues in examples to make them PEP8 compliant. 2010-09-01 18:21:09 -04:00
Lance Stout
0b4320a196 Updated the client and component examples.
The component example now actually uses a config.xml file for its
connection information, and to initialize a roster.
2010-09-01 18:18:30 -04:00
Lance Stout
9bef4b4d4d Move the examples to a top-level examples directory. 2010-09-01 14:47:42 -04:00
Lance Stout
5c3066ba30 Updated all of the matcher classes in sleekxmpp.xmlstream.matcher.
Matchers are now PEP8 compliant and have documentation.
2010-09-01 14:28:43 -04:00
Lance Stout
576eefb097 Fixed line spacing in filesocket.py to please pep8. 2010-09-01 14:25:30 -04:00
Lance Stout
aebd115ba2 A few cleanups to make things simpler. 2010-09-01 14:20:34 -04:00
fritzy
6dfea828be xep-0004 merge should deal with dictionaries 2010-08-31 14:44:24 +00:00
Lance Stout
3749c1b88c Fixed ElementBase.match to match using sub_interface elements. 2010-08-30 17:12:10 -04:00
Lance Stout
998741b87e Fixed typos in ElementBase._fix_ns 2010-08-30 15:25:59 -04:00
Lance Stout
9c62bce206 Updated ElementBase.match to respect namespaces with slashes.
Required adding option to _fix_ns to not propagate namespaces to child elements.
2010-08-30 14:55:30 -04:00
Lance Stout
f5ae27da4f Fix some documentation typos. 2010-08-27 18:16:09 -04:00
Lance Stout
89fb15e896 Updated the suite of handler classes with documentation.
Updated XMLStream to return True or False from removeHandler to indicate if the handler
existed and was removed.

Waiter handlers now unregister themselves after timing out.
2010-08-27 16:42:26 -04:00
Lance Stout
906aa0bd68 Fixed SleekTest compare method to check XML text.
Corrected resulting test failures. All pass again.
2010-08-27 15:48:48 -04:00
Lance Stout
bb6f4af8e2 Added unit tests for StanzaBase. 2010-08-27 12:22:35 -04:00
Lance Stout
6677df39f2 Updated xmlstream.filesocket. 2010-08-27 11:29:48 -04:00
Lance Stout
a2c515bc97 Updated StanzaBase with documentation. 2010-08-27 11:07:20 -04:00
Lance Stout
ca6ce26b0d Added comments to _fix_ns to clarify the cleaning procedure. 2010-08-26 18:40:58 -04:00
Lance Stout
37ff17b0cb Added unit test for _fix_ns for handling namespaces with forward slashes. 2010-08-26 18:27:18 -04:00
Lance Stout
00d7952001 Fixed ElementBase._fix_ns and related methods to respect namespaces which contain forward slashes. 2010-08-26 18:18:00 -04:00
Lance Stout
56766508b3 Fixed indentation in StanzaBase. 2010-08-26 14:19:36 -04:00
Lance Stout
5c59f5baca Clarify ElementBase documentation. 2010-08-26 14:07:09 -04:00
Lance Stout
e16b37d2be Fixed line lengths in ElementBase to comply with PEP8. 2010-08-26 13:55:23 -04:00
Lance Stout
d68bc2ba07 Finished the update of ElementBase with docs and unit tests.
Corrected bugs in equality comparisons between stanzas.
2010-08-26 13:49:36 -04:00
Lance Stout
10298a6eab Updated the remaining ElementBase methods.
Remaining ElementBase todos:
    Write the class documentation for ElementBase.
    Write unit tests for the __magic__ methods.
2010-08-26 10:08:22 -04:00
Lance Stout
a3580dcef9 Fixed ElementBase.match to respect namespaces. 2010-08-25 14:54:09 -04:00
Lance Stout
1eaa9cb28c Updated ElementBase.match and added unit tests. 2010-08-25 14:40:16 -04:00
Lance Stout
5d458bf6c2 Updated ElementBase._delSub and added unit tests.
_delSub can now accept a path and will optionally remove any empty parent elements after deleting the target elements.
2010-08-25 10:52:07 -04:00
Lance Stout
2fa58a74ab Fixed indenting issue. 2010-08-24 09:44:09 -04:00
Lance Stout
c8f406d1b3 Updated ElementBase._setSubText and added unit tests.
_setSubText can now handle elements specified by an XPath expression, and
will build up the element tree as needed, reusing an existing elements in
the path.
2010-08-24 09:37:42 -04:00
Lance Stout
203986dd7c Updated ElementBase._getSubText and added unit tests.
Also added ElementBase._fix_ns() to apply the stanza namespace to elements that don't have a namespace.
2010-08-24 08:55:37 -04:00
fritzy
f4ecf0bac4 fixed a but in stanza_pubsub 2010-08-22 06:08:48 +00:00
fritzy
345656926e added form compatibility with old api, stanzas now bool() to True on 2.x, jid attributes will return '' if not set 2010-08-21 22:48:43 +00:00
Nathan Fritz
c05ddcb7f5 Merge branch 'develop' of git@github.com:fritzy/SleekXMPP into develop 2010-08-19 19:54:09 -07:00
Nathan Fritz
eb9e72fe3e added some xep-0004 compatibility changes 2010-08-19 19:53:56 -07:00
Lance Stout
8a0616b3e0 Updated ElementBase methods _getAttr, _setAttr, and _delAttr with docs and tests. 2010-08-19 20:41:26 -04:00
Lance Stout
b71cfe0492 Small cleanup in ElementBase.__setitem__ 2010-08-19 19:14:18 -04:00
Lance Stout
fac3bca1f6 Updated ElementBase.__delitem__ and added unit tests. 2010-08-19 19:11:12 -04:00
Nathan Fritz
d150b35464 fixed todo merge 2010-08-19 16:09:47 -07:00
Nathan Fritz
21b7109c06 fixed jobs 2010-08-19 16:09:00 -07:00
Lance Stout
e4240dd593 Updated ElementBase.__setitem__ and added unit tests. 2010-08-19 14:21:58 -04:00
Lance Stout
2f6f4fc16d Updated ElementBase.__getitem__ with docs and unit tests. 2010-08-13 21:33:11 -04:00
Lance Stout
fe49b8c377 Updated getStanzaValues and setStanzaValues with docs and unit tests. 2010-08-13 20:05:24 -04:00
Lance Stout
b580a3138d Updated ElementBase.enable and ElementBase.initPlugin 2010-08-13 12:51:07 -04:00
Lance Stout
c20fab0f6c Updated ElementBase.setup, and added unit tests. 2010-08-13 12:24:47 -04:00
Lance Stout
c721fb4126 Added a generic checkStanza method to SleekTest. Updated the other check methods to use it. 2010-08-13 12:23:34 -04:00
Lance Stout
415520200e Updated ElementBase.__init__ 2010-08-13 10:26:33 -04:00
Lance Stout
747001d33c Adjust first level indenting in ElementBase to prepare for cleanup. 2010-08-13 10:15:52 -04:00
Lance Stout
b0fb205c16 Updated registerStanzaPlugin and the XML test type. 2010-08-13 10:12:51 -04:00
Lance Stout
4b52007e8c Cleaned stanzabase imports. 2010-08-12 23:24:09 -04:00
Lance Stout
5da7bd1866 Removed unused xmlcompare.py. 2010-08-12 01:26:01 -04:00
Lance Stout
22134c302b Updated SleekTest with docs and PEP8 style. 2010-08-12 01:25:42 -04:00
Lance Stout
b40a489796 Updated roster stanza with docs and PEP8 style. 2010-08-11 23:32:14 -04:00
Lance Stout
7a5ef28492 Updated SleekTest.streamClose to check that the stream was actually started before closing it.
Updated tests for Iq stanzas to not start a stream for every test; tests now run a lot faster.
The call to streamClose must still be in the tearDown method to ensure it is called in the
case of an error.
2010-08-11 18:41:57 -04:00
Lance Stout
c09e9c702c Updated sleekxmpp.exceptions with PEP8 style and docs. 2010-08-11 18:21:12 -04:00
Lance Stout
48ba7292bc Updated SleekTest to use the new tostring function instead of ET.tostring 2010-08-06 12:04:52 -04:00
Lance Stout
4d1f071f83 Updated the use of tostring in xmlstream.py
Now uses the xmlns and stream parameters to reduce the number of
extra xmlns attributes used in the logging output.

Added self.default_ns to XMLStream just to be safe.
2010-08-05 23:11:22 -04:00
Lance Stout
0d0c044a68 Add unit tests for the tostring function. 2010-08-05 20:57:55 -04:00
Lance Stout
3c0dfb56e6 Update tostring docs to clarify what the xmlns and stanza_ns parameters do. 2010-08-05 20:43:38 -04:00
Lance Stout
e077204a16 Replaced the ToString class with a tostring function.
The sleekxmpp.xmlstream.tostring and sleekxmpp.xmlstream.tostring26 packages
have been merged to sleekxmpp.xmlstream.tostring. The __init__.py file will
import the appropriate tostring function depending on the Python version.

The setup.py file has been updated with the package changes.

ElementBase is now a direct descendent of object and does not subclass ToString.

Stanza objects now return their XML contents for __repr__.
2010-08-05 20:26:41 -04:00
Lance Stout
58f77d898f Updated tests to use a relative import for SleekTest to please Python3.
Fixed some tabs/spaces issues.
2010-08-05 20:23:07 -04:00
Lance Stout
c54466596f Modified sleekxmpp.xmlstream.tostring to import ToString class based on Python version.
The package sleekxmpp.xmlstream.tostring26 remains for now until stanzabase is updated, but is no longer needed.
2010-08-04 14:41:37 -04:00
Lance Stout
aa1dbe97e0 Updated and simplified new JID class to have more documentation and use PEP8 style. 2010-08-04 00:33:28 -04:00
Lance Stout
fec69be731 Update nick stanza with documentation and PEP8 style. 2010-08-03 18:32:53 -04:00
Lance Stout
956fdf6970 Fix whitespace issues, and make some debugging statements clearer. 2010-08-03 18:32:19 -04:00
Lance Stout
183a3f1b87 Updated XHTML-IM stanza with documentation and PEP8 style. 2010-08-03 17:58:18 -04:00
Lance Stout
18683d2b75 Fixed typo in README 2010-08-03 17:32:12 -04:00
Lance Stout
41ab2b8460 Updated presence stanza with documentation and PEP8 style. 2010-08-03 17:30:34 -04:00
Lance Stout
939ae298c2 Updated message stanzas and tests with documentation and PEP8 style. 2010-08-03 12:26:36 -04:00
Nathan Fritz
851e90c572 added dnspython.org to requirements in README 2010-08-03 07:51:52 +00:00
Nathan Fritz
ecde696468 temporary disabled testall methodlength until pep-8 conversion is done 2010-08-03 07:37:58 +00:00
Lance Stout
1cedea2804 Added optional default value to _getAttr. 2010-07-30 14:11:24 -04:00
Lance Stout
cbed8029ba Updated, cleaned, and documented Iq stanza class. Also added unit tests. 2010-07-29 23:58:25 -04:00
Lance Stout
1da3e5b35e Added unit tests for error stanzas. Corrected error in deleting conditions. 2010-07-29 23:55:13 -04:00
Lance Stout
a96a046e27 Remove extra debugging lines and speed up stream testing in SleekTest. 2010-07-29 23:15:49 -04:00
Lance Stout
60a183b011 Added useful imports to the xmlstream, xmlstream.handler, and xmlstream.matcher __init__.py files to make it simpler to import common classes. 2010-07-29 20:18:04 -04:00
Lance Stout
a49f511a2f Added RESPONSE_TIMEOUT constant to sleekxmpp.xmlstream to serve as a single place to specify a default timeout value when waiting for a stanza response. 2010-07-29 20:16:57 -04:00
Lance Stout
25f43bd219 Updated error stanza to be PEP8 compliant and include documentation. 2010-07-29 11:06:10 -04:00
Lance Stout
d148f633f3 Modified ElementBase _getSubText, _setSubText, and _delSubText to
use the namespace in a tag name if one is given and to use
self.namespace otherwise.
2010-07-29 11:04:21 -04:00
Lance Stout
e8e934fa95 Fixed some PEP8 errors in RootStanza (trailing whitespace and line length) 2010-07-29 11:02:42 -04:00
Lance Stout
bd92ef6acf Updated RootStanza to use registerStanzaPlugin, and be PEP8 compliant. 2010-07-28 13:14:41 -04:00
Lance Stout
aa02ecd154 Added notes/ideas/comments on things that can be cleaned/simplified or needs to be expanded before the 1.0 release. 2010-07-27 02:07:22 -04:00
Lance Stout
aad185fe29 Update test to reflect change in reply() method that removes the from attribute. 2010-07-26 21:38:23 -04:00
Nathan Fritz
2b6454786a Merge branch 'experimental' of git@github.com:fritzy/SleekXMPP into experimental 2010-07-26 18:13:54 -07:00
Nathan Fritz
a349a2a317 removed jid from stanzabase to external file 2010-07-26 18:13:34 -07:00
Nathan Fritz
2cb82afc2c updated and moved jid class -- jids now have setters 2010-07-26 18:13:09 -07:00
Lance Stout
c8989c04f3 Replaced traceback calls to use logging.exception where applicable. 2010-07-26 21:02:25 -04:00
Lance Stout
241aba8c76 Merge branch 'experimental' of github.com:fritzy/SleekXMPP into experimental 2010-07-26 19:46:13 -04:00
Lance Stout
ec860bf9e2 Add StateManager as replacement for StateMachine. 2010-07-26 19:44:42 -04:00
Lance Stout
73a3d07ad9 Fix shebang line for testall.py 2010-07-26 19:43:58 -04:00
Lance Stout
07208a3eaf Fix shebang line for testall.py 2010-07-23 19:51:41 -04:00
Lance Stout
d0a5c539d8 Fix shebang lines to use #!/usr/bin/env python instead of hard coding a python version. 2010-07-23 19:47:54 -04:00
Joe Hildebrand
d70a6e6f32 Issue 26. Only set from address in reply() for components 2010-07-20 13:55:48 -07:00
Joe Hildebrand
66e92c6c9f Modified example to take JID and password on command line 2010-07-20 11:33:43 -07:00
Nathan Fritz
ca2c421e6c fixed resource binding element to conform to spec 2010-07-20 11:20:47 -07:00
Nathan Fritz
9fcd2e93a3 don't send resource in bind request if you don't have one 2010-07-20 11:15:59 -07:00
Lance Stout
75afefb5c6 Upated xep_0045 to use old_0004 for now. 2010-07-20 13:23:35 -04:00
Lance Stout
b67b930596 Updated xep_0050 to use old_0004 for now. 2010-07-20 12:27:22 -04:00
Lance Stout
5c9b47afbd Update test_events to use SleekTest to make everything consistent. 2010-07-20 12:22:25 -04:00
Lance Stout
7ad0143687 Updated pubsub stanzas to use xep_0004 stanza objects, and updated tests to match. 2010-07-20 12:18:38 -04:00
Lance Stout
de24e9ed45 Lots of XEP-0004 bug fixes.
Forms have default type of 'form'
setFields now uses a list of tuples instead of a dictionary because ordering is important.
getFields defaults to returning a list of tuples, but the use_dict parameter can change that
2010-07-20 12:16:57 -04:00
Lance Stout
9724efa123 Please tab nanny. 2010-07-20 12:16:06 -04:00
Lance Stout
690eaf8d3c Updated license notices to use the correct MIT format. Also corrected references to nonexistant license.txt to LICENSE. 2010-07-20 11:19:49 -04:00
Lance Stout
f505e229d6 Updated message stanza tests. 2010-07-20 01:56:18 -04:00
Lance Stout
9ca4bba2de Update XEP-0128 to use new xep_0004 2010-07-20 00:34:24 -04:00
Lance Stout
bb927c7e6a Updated presence stanza to include a 'show' interface. Presence stanza tests updated accordingly. 2010-07-20 00:04:34 -04:00
Lance Stout
14f1c3ba51 Updated SleekTest to implement the checkPresence method.
Also, removed unnecessary TestStream class and shortened timeout during stream connection.
2010-07-19 23:58:33 -04:00
Lance Stout
278a8bb443 Removed outdated MANIFEST file. Setuptools will generate a new one when needed. 2010-07-19 23:57:21 -04:00
Nathan Fritz
85ee30539d more set/get Values changes 2010-07-19 16:26:25 -07:00
Nathan Fritz
f74baf1c23 updated sleektest to use new stanza get/set values api 2010-07-19 16:25:01 -07:00
Lance Stout
b5a14a0190 Can now pass a name to add_handler so that the handler can be reliably removed later.
Updated uses of add_handler to include a name.
2010-07-19 19:19:33 -04:00
Nathan Fritz
fec8578cf6 stanza should not have setValues/getValues because that conflicts with attribute accessors 2010-07-19 15:38:48 -07:00
Nathan Fritz
f80b3285d4 indent problem on stanzabase 2010-07-19 14:57:21 -07:00
Nathan Fritz
130a148d34 added fromXML/getXML compatiblity to the new xep-0004 w/ deprecated warnings 2010-07-19 13:53:41 -07:00
Nathan Fritz
16104b6e56 made Lance's new XEP-4 stanzas the default, and put xep-0004 as old_0004 2010-07-19 13:36:28 -07:00
Lance Stout
d5e42ac0e7 Condensed all of the stanzaPlugin functions into a single registerStanzaPlugin function.
Updated plugins and tests to use new function.
2010-07-19 13:58:53 -04:00
Lance Stout
e6bec8681e Added implementation for XEP-0128 Service Discovery Extensions.
Uses the alt_0004 plugin for jabberdata stanza objects.
2010-07-19 04:22:31 -04:00
Lance Stout
797e92a6a3 Fixed error in updateRoster when the name keyword parameter is left out.
The Roster stanza object builds item elements manually, and did not handle the
case where the name attribute is set to None, which would crash SleekXMPP.
2010-07-19 04:12:54 -04:00
Lance Stout
1ef112966b Merge branch 'develop' of git://github.com/fritzy/SleekXMPP into develop 2010-07-19 04:02:28 -04:00
Nathan Fritz
078c71ed3f accidental debugging return left in the code from last commit 2010-07-15 14:25:10 -07:00
Nathan Fritz
bae082f437 fixed updateRoster and delRosterItem 2010-07-15 11:53:35 -07:00
Lance Stout
35212c7991 Updated SleekTest to be able to simulate and test interactions with an XML stream. 2010-07-14 15:32:14 -04:00
Lance Stout
48f0843ace Added initial stanza object version of the xep_0004 plugin. Items/reported elements still need to be unit tested 2010-07-14 11:59:58 -04:00
Lance Stout
b1c997be1d Reworked the Gmail notification plugin to use stanza objects and expose more information. 2010-07-11 22:01:51 -04:00
Lance Stout
d0cb400c54 Fixed tabs to please tab nanny. 2010-07-11 21:43:51 -04:00
Lance Stout
7f8179d91e Refactored unit tests for XEP-0030, XEP-0033, and XEP-0085 to use the new SleekTest class. 2010-06-27 17:47:32 -04:00
Lance Stout
37ada49802 Fixed indentation to please tab nanny during unit tests. 2010-06-27 17:39:16 -04:00
Lance Stout
5c76d969f7 Added a new SleekTest class that provides useful methods for test cases.
Can now use: (where self is a SleekTest instance)
self.stanzaPlugin(stanza, plugin)
self.Message()  \
self.Iq()        > Just like basexmpp.Message(), etc.
self.Presence() /
self.checkMessage(msg, xmlstring)
self.checkIq(iq, xmlstring)
self.checkPresence(pres, xmlstring) <- Not implemented yet, but stub is there.

The check* methods also accept a use_values keyword argument that defaults to True.
When this value is True, an additional test is executed by creating a stanza using
getValues() and setValues(). Since some stanza objects can override these two methods,
disabling this test is sometimes required.
2010-06-27 17:33:43 -04:00
Lance Stout
059cc9ccc4 Fixed several errors in xep_0033 plugin.
The method getAddresses was removing addresses by mistake.
Several instances of using self.attrib instead of self.xml.attrib.
2010-06-27 17:32:16 -04:00
Lance Stout
309c9e74eb Fixed error in setState() method. 2010-06-27 16:34:48 -04:00
Lance Stout
6041cd1952 Fixed typo 2010-06-27 16:33:59 -04:00
Lance Stout
acb53ba371 Fixed tab and spacing issue to please the Tab Nanny during unit tests. 2010-06-27 10:14:21 -04:00
Lance Stout
646a609c0b Added plugin and tests for XEP-0033, Extended Stanza Addresses.
XEP-0033 can be useful for interacting with XMPP<->Email gateways.
2010-06-22 23:22:50 -04:00
Lance Stout
8bb0f5e34c Needed to use copy.deepcopy() to copy XML objects to make sure that the entire tree is copied. 2010-06-07 19:55:39 -04:00
Lance Stout
3c939313d2 Modified basexmpp.event() to pass a copy of the event data to each handler. 2010-06-06 23:19:07 -04:00
Lance Stout
9962f1a664 Added a __copy__ method to both ElementBase and StanzaBase.
Stanzas may now be copied using copy.copy(), which will be useful to prevent
stanza objects from being shared between event handlers.
2010-06-06 23:12:54 -04:00
Lance Stout
253de8518c Modified xmlstream.py to pass a clean stanza object to each stream handler.
The previous version passed the same stanza object to each registered handler,
which can cause issues when the stanza object is modified by one handler. The next
handler receives the stanza with the modifications, not the original stanza.
2010-06-03 22:56:57 -04:00
Nathan Fritz
a38735cb2a added very, very, very basic atom stanza 2010-06-02 15:54:44 -07:00
Lance Stout
e700a54d11 Return result of iq.send() for disco requests. Events are still triggered, but now the caller can determine if there was a timeout. 2010-06-02 15:59:10 -04:00
Lance Stout
6469cdb4ca Merge branch 'develop' of git://github.com/fritzy/SleekXMPP into develop 2010-06-02 15:57:18 -04:00
Nathan Fritz
18e27d65ce Merge branch 'develop' of github.com:fritzy/SleekXMPP into develop 2010-06-01 21:45:15 -07:00
Nathan Fritz
0c39567f20 hack fix for session before bind 2010-06-01 21:44:54 -07:00
Nathan Fritz
f5491c901f if binding and session are advertised in the same go, do session first 2010-06-01 21:40:52 -07:00
Lance stout
f5cae85af5 Make sure that the id parameter used in xmpp.makeIq is converted to a string.
Otherwise, SleekXMPP will barf on trying to serialize an integer when it expects text.
2010-06-01 10:52:37 -04:00
Lance stout
01e8040a07 Added additional parameter to xep_0030's getInfo and getItems methods.
By using dfrom, a server component may send disco requests using any of its JIDS.
2010-06-01 10:51:03 -04:00
Nathan Fritz
aa916c9ac8 included jobs plugin 2010-05-31 13:57:39 -07:00
Lance stout
332eea3b3b Make sure that the node is alway set in disco responses. 2010-05-31 13:35:15 -04:00
Lance stout
109af1b1b6 Merge branch 'xep_0085' into develop 2010-05-31 13:31:11 -04:00
Lance stout
629f6e76a9 Added implementation and tests for XEP-0085 - Chat State Notifications.
Chat states may be set using:

msg['chat_state'].active()
msg['chat_state'].composing()
msg['chat_state'].gone()
msg['chat_state'].inactive()
msg['chat_state'].paused()

Checking a chat state can be done with either:

msg['chat_state'].getState()
msg['chat_state'].name

When a message with a chat state is receieved, the following events
may occur:

chatstate_active
chatstate_composing
chatstate_gone
chatstate_inactive
chatstate_paused

where the event data is the message stanza. Note that currently these
events are also triggered for messages sent by SleekXMPP, not just those
received.
2010-05-31 13:24:14 -04:00
Nathan Fritz
82a3918aa4 Scheduler waits too longer, and pubsubstate registration was backwards 2010-05-31 03:36:25 -07:00
Lance stout
cff3079a04 Added missing 'internal-server-error' condition to error stanza interface. 2010-05-31 05:30:50 +08:00
Lance stout
4f864a07f5 Touched up the style of creating an Iq stanza. 2010-05-31 05:30:49 +08:00
Lance stout
938066bd50 Added 'resource-constraint' to the list of error conditions. 2010-05-31 05:30:48 +08:00
Lance Stout
9fee87c258 Added unit tests for the new XEP-0030 stanza objects. All pass.
(cherry picked from commit e1b814f27bf160f20bb30c315ca30769d217482d)
2010-05-31 05:30:47 +08:00
Lance Stout
fd573880eb Updated the XEP-0030 plugin to work with stanza objects instead of manipulating XML directly.
Four new events have been added:
  disco_info - A disco#info result has been received
  disco_info_request - A disco#info request has been received
  disco_items - A disco#items result has been received
  disco_items_request - A disco#items request has been received

For disco_info_request and disco_items_request two default handlers are registered. These handlers will only run if they are the only handler for these two events so that multiple responses are not returned and cause errors.

In your own handlers for these two events, you can call the default handlers to preserve the static node behaviour as so:
  self.plugin['xep_0030'].handle_disco_info(iq, True)

The forwarded=True will disable the check for other registered handlers.

Agents can now dynamically respond to disco requests by using these events.
(cherry picked from commit 0fc3381492a8bd75e6a9858539a972334881d8ff)
2010-05-31 05:30:45 +08:00
Nathan Fritz
2f1ba368e2 control-c fixes 2010-05-28 19:19:28 -07:00
Nathan Fritz
bde1818400 added pubsubjobs test 2010-05-27 04:59:41 -07:00
Nathan Fritz
3a28f9e5d2 added pubsub state stanzas and scheduled events 2010-05-27 04:58:57 -07:00
Nathan Fritz
0bda5fd3f2 adding scheduler 2010-05-26 18:32:28 -07:00
Nathan Fritz
1e3a6e1b5f added muc room to readme 2010-05-26 11:46:56 -07:00
Nathan Fritz
fa92bc866b fixed dns unicode problem 2010-05-26 11:37:01 -07:00
Nathan Fritz
f4bc9d9722 plugins now are checked for post_init having ran when process() is called 2010-05-26 10:51:51 -07:00
Hernan E Grecco
9cfe19c1e1 Changed example.py to register first Xep_0030.
This a simple fix to prevent getting a key error as many plugins add
features to Xep_0030. A better fix would be to call pos_init after all
 plugins are loaded. An even better fix would be to define dependencies
for each plugin and registering on demand.
2010-05-26 06:49:01 +08:00
Hernan E Grecco
f18c790824 Fixed error registering a plugin. To add a feature to another plugin, it should look into xmpp.plugin dict 2010-05-26 06:49:01 +08:00
Nathan Fritz
f165b4b52b Merge branch 'master' of git@github.com:fritzy/SleekXMPP 2010-05-24 19:34:49 -07:00
Nathan Fritz
7ebc006516 updated README, index fix for component 2010-05-24 19:33:24 -07:00
Lance Stout
5ca4ede5ac Added a flag to registerPlugin to control calling the plugin's post_init method. 2010-05-25 07:28:48 +08:00
Lance Stout
35f4ef3452 Modified the return values for several methods so that they can be chained.
For example:

    iq.reply().error().setPayload(something.xml).send()
2010-05-25 07:28:43 +08:00
Lance Stout
828cba875f Added the error attribute 'code' to the Error object interface. 2010-05-25 07:28:43 +08:00
Nathan Fritz
3920ee3941 added plugin indexing to components 2010-05-24 14:27:13 -07:00
Nathan Fritz
feaa7539af added test_events and testing new del_event_handler 2010-05-20 13:09:04 -07:00
Lance Stout
c004f042f9 Added del_event_handler to remove handler functions for a given event.
All registered handlers for the event which use the given function will
be removed.

Using this method allows agents to reconfigure their behaviour on the fly
without needing to add extra state information to event handling functions.
2010-05-21 03:54:48 +08:00
Nathan Fritz
ae41c08fec added test for unsolicided unavailable presence and fixed bug to make it pass 2010-05-12 18:07:20 -07:00
Nathan Fritz
223507f36f fixed a rather large memory leak 2010-05-12 13:45:36 -07:00
Nathan Fritz
8515cef117 refactored presence tracking and fixed jidInRoom 2010-05-11 15:12:13 -07:00
Nathan Fritz
9d76e7353a Merge branch 'master' of git@github.com:fritzy/SleekXMPP 2010-05-11 12:21:02 -07:00
Nathan Fritz
2f8c1954f0 use pubsub settings 2010-05-11 12:20:57 -07:00
Nathan Fritz
d40e49397b added collection tests 2010-04-30 08:21:32 -07:00
Nathan Fritz
c4fa3ab654 pubsub test work 2010-04-23 01:34:28 -07:00
Nathan Fritz
e2f841146a forgot to add file required to pass testall.py 2010-04-22 23:40:50 -07:00
Nathan Fritz
602a6d8491 bugfixes and continuing to work on pubsub tests 2010-04-22 21:24:28 -07:00
Nathan Fritz
37b571c55a added pubsub#event stanzas, multi-subtypes iterable stanzas, pubsub#event test coverage 2010-04-21 23:51:37 -07:00
Nathan Fritz
2a30e3fe0c started work on pubsub#event stanzas 2010-04-20 00:53:13 -07:00
Nathan Fritz
212660091f added pubsub tests and fixed match on iterator error 2010-04-19 01:03:27 -07:00
Nathan Fritz
35c157f9d8 changed_status event now happens with got_offline/got_online events 2010-04-18 21:16:47 -07:00
Nathan Fritz
09aae38117 initial add of pubsub server interaction tests 2010-04-14 01:25:14 -07:00
Nathan Fritz
80e7e0d0ee adding tests, fixed stanzapath matching to match keys, fixed pubsub#owner stanzas 2010-04-14 01:23:17 -07:00
Nathan Fritz
2f9f649d98 bugfix for .disconnect() hanging 2010-04-13 19:48:35 -07:00
Nathan Fritz
fef511fd51 bugfix for .disconnect() hanging 2010-04-13 19:35:47 -07:00
Matthias Rieber
85c3d97d2a fixed presence stanza. priority can be set. 2010-04-08 15:15:15 +08:00
Nathan Fritz
2384858f5e fixed html-im stanza plugin 2010-04-07 23:56:44 -07:00
Nathan Fritz
dd77d2165d added missing docstrings to tests and added deprecated warning to basexmpp.send when using xml mask waiting 2010-04-07 23:32:15 -07:00
Nathan Fritz
ecd5a172ed replaced usage of deprecated iq result on send. Fixed old send result to use stanzas instead of ElementTree 2010-04-07 23:10:32 -07:00
368 changed files with 45686 additions and 4348 deletions

7
.gitignore vendored
View File

@@ -1,2 +1,9 @@
*.pyc
build/
dist/
MANIFEST
docs/_build/
*.swp
.tox/
.coverage
sleekxmpp.egg-info/

10
INSTALL
View File

@@ -1,8 +1,12 @@
Pre-requisites:
Python 3.1 or 2.6
- Python 3.1 or 2.6
Install:
python3 setup.py install
> python3 setup.py install
Root install:
sudo python3 setup.py install
> sudo python3 setup.py install
To test:
> cd examples
> python echo_client.py -v -j [USER@example.com] -p [PASSWORD]

152
LICENSE
View File

@@ -1,4 +1,4 @@
Copyright (c) 2010 ICRL
Copyright (c) 2010 Nathanael C. Fritz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -17,3 +17,153 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Licenses of Bundled Third Party Code
------------------------------------
dateutil - Extensions to the standard python 2.3+ datetime module.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copyright (c) 2003-2011 - Gustavo Niemeyer <gustavo@niemeyer.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
fixed_datetime
~~~~~~~~~~~~~~
Copyright (c) 2008, Red Innovation Ltd., Finland
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Red Innovation nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY RED INNOVATION ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL RED INNOVATION BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
OrderedDict - A port of the Python 2.7+ OrderedDict to Python 2.6
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copyright (c) 2009 Raymond Hettinger
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
SUELTA A PURE-PYTHON SASL CLIENT LIBRARY
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This software is subject to "The MIT License"
Copyright 2007-2010 David Alan Cridland
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
python-gnupg: A Python wrapper for the GNU Privacy Guard
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Copyright (c) 2008-2012 by Vinay Sajip.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name(s) of the copyright holder(s) may not be used to endorse or
promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,39 +0,0 @@
setup.py
sleekxmpp/__init__.py
sleekxmpp/basexmpp.py
sleekxmpp/clientxmpp.py
sleekxmpp/example.py
sleekxmpp/plugins/__init__.py
sleekxmpp/plugins/base.py
sleekxmpp/plugins/gmail_notify.py
sleekxmpp/plugins/xep_0004.py
sleekxmpp/plugins/xep_0009.py
sleekxmpp/plugins/xep_0030.py
sleekxmpp/plugins/xep_0045.py
sleekxmpp/plugins/xep_0050.py
sleekxmpp/plugins/xep_0060.py
sleekxmpp/plugins/xep_0078.py
sleekxmpp/plugins/xep_0086.py
sleekxmpp/plugins/xep_0092.py
sleekxmpp/plugins/xep_0199.py
sleekxmpp/stanza/__init__.py
sleekxmpp/stanza/iq.py
sleekxmpp/stanza/message.py
sleekxmpp/stanza/presence.py
sleekxmpp/xmlstream/__init__.py
sleekxmpp/xmlstream/stanzabase.py
sleekxmpp/xmlstream/statemachine.py
sleekxmpp/xmlstream/test.py
sleekxmpp/xmlstream/testclient.py
sleekxmpp/xmlstream/xmlstream.py
sleekxmpp/xmlstream/handler/__init__.py
sleekxmpp/xmlstream/handler/base.py
sleekxmpp/xmlstream/handler/callback.py
sleekxmpp/xmlstream/handler/waiter.py
sleekxmpp/xmlstream/handler/xmlcallback.py
sleekxmpp/xmlstream/handler/xmlwaiter.py
sleekxmpp/xmlstream/matcher/__init__.py
sleekxmpp/xmlstream/matcher/base.py
sleekxmpp/xmlstream/matcher/many.py
sleekxmpp/xmlstream/matcher/xmlmask.py
sleekxmpp/xmlstream/matcher/xpath.py

6
MANIFEST.in Normal file
View File

@@ -0,0 +1,6 @@
include README.rst
include LICENSE
include testall.py
recursive-include docs Makefile *.bat *.py *.rst *.css *.ttf *.png
recursive-include examples *.py
recursive-include tests *.py

37
README
View File

@@ -1,37 +0,0 @@
SleekXMPP is an XMPP library written for Python 3.x (with 2.6 compatibility).
Featured in examples in XMPP: The Definitive Guide by Kevin Smith, Remko Tronçon, and Peter Saint-Andre
SleekXMPP has several design goals/philosophies:
- Low number of dependencies.
- Every XEP as a plugin.
- Rewarding to work with.
The goals for 1.0 include (and we're getting close):
- Nearly Full test coverage of stanzas.
- Wide range of functional tests.
- Stanza objects for all interaction with the stream
- Documentation on using and extending SleekXMPP.
- Complete documentation on all implemented stanza objects
- Documentation on all examples used in XMPP: The Definitive Guide
1.1 will include:
- More functional and unit tests
- PEP-8 compliance
- XEP-225 support
Since 0.2, here's the Changelog:
- MANY bugfixes
- Re-implementation of handlers/threading to greatly simplify and remove bugs (no more spawning threads in handlers)
- Stanza objects for jabber:client and all implemented XEPs
- Raising XMPPError for jabber:client and extended errors in handlers
- Robust error handling and better insurance of iq responses
- Stanza objects have made life a lot easier!
- Massive audit/cleanup.
Credits
----------------
Main Author: Nathan Fritz fritz@netflint.net
XEP-0045 original implementation: Kevin Smith
Patches: Remko Tronçon
Feel free to add fritzy@netflint.net to your roster for direct support and comments.

180
README.rst Normal file
View File

@@ -0,0 +1,180 @@
SleekXMPP
#########
SleekXMPP is an MIT licensed XMPP library for Python 2.6/3.1+,
and is featured in examples in
`XMPP: The Definitive Guide <http://oreilly.com/catalog/9780596521271>`_
by Kevin Smith, Remko Tronçon, and Peter Saint-Andre. If you've arrived
here from reading the Definitive Guide, please see the notes on updating
the examples to the latest version of SleekXMPP.
SleekXMPP's design goals and philosphy are:
**Low number of dependencies**
Installing and using SleekXMPP should be as simple as possible, without
having to deal with long dependency chains.
As part of reducing the number of dependencies, some third party
modules are included with SleekXMPP in the ``thirdparty`` directory.
Imports from this module first try to import an existing installed
version before loading the packaged version, when possible.
**Every XEP as a plugin**
Following Python's "batteries included" approach, the goal is to
provide support for all currently active XEPs (final and draft). Since
adding XEP support is done through easy to create plugins, the hope is
to also provide a solid base for implementing and creating experimental
XEPs.
**Rewarding to work with**
As much as possible, SleekXMPP should allow things to "just work" using
sensible defaults and appropriate abstractions. XML can be ugly to work
with, but it doesn't have to be that way.
Get the Code
------------
Get the latest stable version from PyPI::
pip install sleekxmpp
The latest source code for SleekXMPP may be found on `Github
<http://github.com/fritzy/SleekXMPP>`_. Releases can be found in the
``master`` branch, while the latest development version is in the
``develop`` branch.
**Latest Release**
- `1.1.5 <http://github.com/fritzy/SleekXMPP/zipball/1.1.5>`_
**Develop Releases**
- `Latest Develop Version <http://github.com/fritzy/SleekXMPP/zipball/develop>`_
**Older Stable Releases**
- `1.0 <http://github.com/fritzy/SleekXMPP/zipball/1.0>`_
Installing DNSPython
---------------------
If you are using Python3 and wish to use dnspython, you will have to checkout and
install the ``python3`` branch::
git clone http://github.com/rthalley/dnspython
cd dnspython
git checkout python3
python3 setup.py install
Discussion
----------
A mailing list and XMPP chat room are available for discussing and getting
help with SleekXMPP.
**Mailing List**
`SleekXMPP Discussion on Google Groups <http://groups.google.com/group/sleekxmpp-discussion>`_
**Chat**
`sleek@conference.jabber.org <xmpp:sleek@conference.jabber.org?join>`_
Documentation and Testing
-------------------------
Documentation can be found both inline in the code, and as a Sphinx project in ``/docs``.
To generate the Sphinx documentation, follow the commands below. The HTML output will
be in ``docs/_build/html``::
cd docs
make html
open _build/html/index.html
To run the test suite for SleekXMPP::
python testall.py
The SleekXMPP Boilerplate
-------------------------
Projects using SleekXMPP tend to follow a basic pattern for setting up client/component
connections and configuration. Here is the gist of the boilerplate needed for a SleekXMPP
based project. See the documetation or examples directory for more detailed archetypes for
SleekXMPP projects::
import logging
from sleekxmpp import ClientXMPP
from sleekxmpp.exceptions import IqError, IqTimeout
class EchoBot(ClientXMPP):
def __init__(self, jid, password):
ClientXMPP.__init__(self, jid, password)
self.add_event_handler("session_start", self.session_start)
self.add_event_handler("message", self.message)
# If you wanted more functionality, here's how to register plugins:
# self.register_plugin('xep_0030') # Service Discovery
# self.register_plugin('xep_0199') # XMPP Ping
# Here's how to access plugins once you've registered them:
# self['xep_0030'].add_feature('echo_demo')
# If you are working with an OpenFire server, you will
# need to use a different SSL version:
# import ssl
# self.ssl_version = ssl.PROTOCOL_SSLv3
def session_start(self, event):
self.send_presence()
self.get_roster()
# Most get_*/set_* methods from plugins use Iq stanzas, which
# can generate IqError and IqTimeout exceptions
#
# try:
# self.get_roster()
# except IqError as err:
# logging.error('There was an error getting the roster')
# logging.error(err.iq['error']['condition'])
# self.disconnect()
# except IqTimeout:
# logging.error('Server is taking too long to respond')
# self.disconnect()
def message(self, msg):
if msg['type'] in ('chat', 'normal'):
msg.reply("Thanks for sending\n%(body)s" % msg).send()
if __name__ == '__main__':
# Ideally use optparse or argparse to get JID,
# password, and log level.
logging.basicConfig(level=logging.DEBUG,
format='%(levelname)-8s %(message)s')
xmpp = EchoBot('somejid@example.com', 'use_getpass')
xmpp.connect()
xmpp.process(block=True)
Credits
-------
**Main Author:** Nathan Fritz
`fritzy@netflint.net <xmpp:fritzy@netflint.net?message>`_,
`@fritzy <http://twitter.com/fritzy>`_
Nathan is also the author of XMPPHP and `Seesmic-AS3-XMPP
<http://code.google.com/p/seesmic-as3-xmpp/>`_, and a former member of
the XMPP Council.
**Co-Author:** Lance Stout
`lancestout@gmail.com <xmpp:lancestout@gmail.com?message>`_,
`@lancestout <http://twitter.com/lancestout>`_
**Contributors:**
- Brian Beggs (`macdiesel <http://github.com/macdiesel>`_)
- Dann Martens (`dannmartens <http://github.com/dannmartens>`_)
- Florent Le Coz (`louiz <http://github.com/louiz>`_)
- Kevin Smith (`Kev <http://github.com/Kev>`_, http://kismith.co.uk)
- Remko Tronçon (`remko <http://github.com/remko>`_, http://el-tramo.be)
- Te-jé Rogers (`te-je <http://github.com/te-je>`_)
- Thom Nichols (`tomstrummer <http://github.com/tomstrummer>`_)

1
docs/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
_build/*

130
docs/Makefile Normal file
View File

@@ -0,0 +1,130 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/SleekXMPP.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/SleekXMPP.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/SleekXMPP"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/SleekXMPP"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
make -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

452
docs/_static/agogo.css vendored Normal file
View File

@@ -0,0 +1,452 @@
/*
* agogo.css_t
* ~~~~~~~~~~~
*
* Sphinx stylesheet -- agogo theme.
*
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
* {
margin: 0px;
padding: 0px;
}
body {
font-family: "Verdana", Arial, sans-serif;
line-height: 1.4em;
color: black;
background-color: #eeeeec;
}
/* Page layout */
div.header, div.content, div.footer {
width: 70em;
margin-left: auto;
margin-right: auto;
}
div.header-wrapper {
background: url(bgtop.png) top left repeat-x;
border-bottom: 3px solid #2e3436;
}
/* Default body styles */
a {
color: #ce5c00;
}
div.bodywrapper a, div.footer a {
text-decoration: underline;
}
.clearer {
clear: both;
}
.left {
float: left;
}
.right {
float: right;
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
h1, h2, h3, h4 {
font-family: "Georgia", "Times New Roman", serif;
font-weight: normal;
color: #3465a4;
margin-bottom: .8em;
}
h1 {
color: #204a87;
}
h2 {
padding-bottom: .5em;
border-bottom: 1px solid #3465a4;
}
a.headerlink {
visibility: hidden;
color: #dddddd;
padding-left: .3em;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink {
visibility: visible;
}
img {
border: 0;
}
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 2px 7px 1px 7px;
border-left: 0.2em solid black;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
dt:target, .highlighted {
background-color: #fbe54e;
}
/* Header */
div.header {
padding-top: 10px;
padding-bottom: 10px;
}
div.header h1 {
font-family: "Georgia", "Times New Roman", serif;
font-weight: normal;
font-size: 180%;
letter-spacing: .08em;
}
div.header h1 a {
color: white;
}
div.header div.rel {
margin-top: 1em;
}
div.header div.rel a {
color: #fcaf3e;
letter-spacing: .1em;
text-transform: uppercase;
}
p.logo {
float: right;
}
img.logo {
border: 0;
}
/* Content */
div.content-wrapper {
background-color: white;
padding-top: 20px;
padding-bottom: 20px;
}
div.document {
width: 50em;
float: left;
}
div.body {
padding-right: 2em;
text-align: justify;
}
div.document ul {
margin: 1.5em;
list-style-type: square;
}
div.document dd {
margin-left: 1.2em;
margin-top: .4em;
margin-bottom: 1em;
}
div.document .section {
margin-top: 1.7em;
}
div.document .section:first-child {
margin-top: 0px;
}
div.document div.highlight {
padding: 3px;
background-color: #eeeeec;
border-top: 2px solid #dddddd;
border-bottom: 2px solid #dddddd;
margin-top: .8em;
margin-bottom: .8em;
}
div.document h2 {
margin-top: .7em;
}
div.document p {
margin-bottom: .5em;
}
div.document li.toctree-l1 {
margin-bottom: 1em;
}
div.document .descname {
font-weight: bold;
}
div.document .docutils.literal {
background-color: #eeeeec;
padding: 1px;
}
div.document .docutils.xref.literal {
background-color: transparent;
padding: 0px;
}
div.document blockquote {
margin: 1em;
}
div.document ol {
margin: 1.5em;
}
/* Sidebar */
div.sidebar {
width: 20em;
float: right;
font-size: .9em;
}
div.sidebar a, div.header a {
text-decoration: none;
}
div.sidebar a:hover, div.header a:hover {
text-decoration: underline;
}
div.sidebar h3 {
color: #2e3436;
text-transform: uppercase;
font-size: 130%;
letter-spacing: .1em;
}
div.sidebar ul {
list-style-type: none;
}
div.sidebar li.toctree-l1 a {
display: block;
padding: 1px;
border: 1px solid #dddddd;
background-color: #eeeeec;
margin-bottom: .4em;
padding-left: 3px;
color: #2e3436;
}
div.sidebar li.toctree-l2 a {
background-color: transparent;
border: none;
margin-left: 1em;
border-bottom: 1px solid #dddddd;
}
div.sidebar li.toctree-l3 a {
background-color: transparent;
border: none;
margin-left: 2em;
border-bottom: 1px solid #dddddd;
}
div.sidebar li.toctree-l2:last-child a {
border-bottom: none;
}
div.sidebar li.toctree-l1.current a {
border-right: 5px solid #fcaf3e;
}
div.sidebar li.toctree-l1.current li.toctree-l2 a {
border-right: none;
}
/* Footer */
div.footer-wrapper {
background: url(bgfooter.png) top left repeat-x;
border-top: 4px solid #babdb6;
padding-top: 10px;
padding-bottom: 10px;
min-height: 80px;
}
div.footer, div.footer a {
color: #888a85;
}
div.footer .right {
text-align: right;
}
div.footer .left {
text-transform: uppercase;
}
/* Styles copied from basic theme */
img.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
img.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left;
}
.align-center {
clear: both;
text-align: center;
}
.align-right {
text-align: right;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin: 10px 0 0 20px;
padding: 0;
}
ul.search li {
padding: 5px 0 5px 20px;
background-image: url(file.png);
background-repeat: no-repeat;
background-position: 0 7px;
}
ul.search li a {
font-weight: bold;
}
ul.search li div.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- index page ------------------------------------------------------------ */
table.contentstable {
width: 90%;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}
/* -- general index --------------------------------------------------------- */
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable dl, table.indextable dd {
margin-top: 0;
margin-bottom: 0;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
/* -- viewcode extension ---------------------------------------------------- */
.viewcode-link {
float: right;
}
.viewcode-back {
float: right;
font-family:: "Verdana", Arial, sans-serif;
}
div.viewcode-block:target {
margin: -1px -3px;
padding: 0 3px;
background-color: #f4debf;
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
}

532
docs/_static/basic.css vendored Normal file
View File

@@ -0,0 +1,532 @@
/*
* basic.css
* ~~~~~~~~~
*
* Sphinx stylesheet -- basic theme.
*
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
/* -- main layout ----------------------------------------------------------- */
div.clearer {
clear: both;
}
/* -- relbar ---------------------------------------------------------------- */
div.related {
width: 100%;
font-size: 90%;
}
div.related h3 {
display: none;
}
div.related ul {
margin: 0;
padding: 0 0 0 10px;
list-style: none;
}
div.related li {
display: inline;
}
div.related li.right {
float: right;
margin-right: 5px;
}
/* -- sidebar --------------------------------------------------------------- */
div.sphinxsidebarwrapper {
padding: 10px 5px 0 10px;
}
div.sphinxsidebar {
float: left;
width: 230px;
margin-left: -100%;
font-size: 90%;
}
div.sphinxsidebar ul {
list-style: none;
}
div.sphinxsidebar ul ul,
div.sphinxsidebar ul.want-points {
margin-left: 20px;
list-style: square;
}
div.sphinxsidebar ul ul {
margin-top: 0;
margin-bottom: 0;
}
div.sphinxsidebar form {
margin-top: 10px;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
img {
border: 0;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin: 10px 0 0 20px;
padding: 0;
}
ul.search li {
padding: 5px 0 5px 20px;
background-image: url(file.png);
background-repeat: no-repeat;
background-position: 0 7px;
}
ul.search li a {
font-weight: bold;
}
ul.search li div.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- index page ------------------------------------------------------------ */
table.contentstable {
width: 90%;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}
/* -- general index --------------------------------------------------------- */
table.indextable {
width: 100%;
}
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable dl, table.indextable dd {
margin-top: 0;
margin-bottom: 0;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
div.modindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
div.genindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
/* -- general body styles --------------------------------------------------- */
a.headerlink {
visibility: hidden;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink {
visibility: visible;
}
div.body p.caption {
text-align: inherit;
}
div.body td {
text-align: left;
}
.field-list ul {
padding-left: 1em;
}
.first {
margin-top: 0 !important;
}
p.rubric {
margin-top: 30px;
font-weight: bold;
}
img.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
img.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left;
}
.align-center {
clear: both;
text-align: center;
}
.align-right {
text-align: right;
}
/* -- sidebars -------------------------------------------------------------- */
div.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
padding: 7px 7px 0 7px;
background-color: #efefef;
width: 40%;
float: right;
-mox-border-radius: 4px;
-webkit-border-radius: 4px;
border-radius: 4px;
}
p.sidebar-title {
font-weight: bold;
text-transform: uppercase;
}
/* -- topics ---------------------------------------------------------------- */
div.topic {
border: 1px solid #ccc;
padding: 7px 7px 0 7px;
margin: 10px 0 10px 0;
}
p.topic-title {
font-size: 1.1em;
font-weight: bold;
margin-top: 10px;
}
/* -- admonitions ----------------------------------------------------------- */
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 7px;
}
div.admonition dt {
font-weight: bold;
}
div.admonition dl {
margin-bottom: 0;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
div.body p.centered {
text-align: center;
margin-top: 25px;
}
/* -- tables ---------------------------------------------------------------- */
table.docutils {
border: 0;
border-collapse: collapse;
}
table.docutils td, table.docutils th {
padding: 1px 8px 1px 5px;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #aaa;
}
table.field-list td, table.field-list th {
border: 0 !important;
}
table.footnote td, table.footnote th {
border: 0 !important;
}
th {
text-align: left;
padding-right: 5px;
}
table.citation {
border-left: solid 1px gray;
margin-left: 1px;
}
table.citation td {
border-bottom: none;
}
/* -- other body styles ----------------------------------------------------- */
ol.arabic {
list-style: decimal;
}
ol.loweralpha {
list-style: lower-alpha;
}
ol.upperalpha {
list-style: upper-alpha;
}
ol.lowerroman {
list-style: lower-roman;
}
ol.upperroman {
list-style: upper-roman;
}
dl {
margin-bottom: 15px;
}
dd p {
margin-top: 0px;
}
dd ul, dd table {
margin-bottom: 10px;
}
dd {
margin-top: 3px;
margin-bottom: 10px;
margin-left: 30px;
}
dt:target, .highlighted {
}
dl.glossary dt {
font-weight: bold;
font-size: 1.1em;
}
.field-list ul {
margin: 0;
padding-left: 1em;
}
.field-list p {
margin: 0;
}
.refcount {
color: #060;
}
.optional {
font-size: 1.3em;
}
.versionmodified {
font-style: italic;
}
.system-message {
background-color: #fda;
padding: 5px;
border: 3px solid red;
}
.footnote:target {
background-color: #ffa;
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
.guilabel, .menuselection {
font-family: sans-serif;
}
.accelerator {
text-decoration: underline;
}
.classifier {
font-style: oblique;
}
/* -- code displays --------------------------------------------------------- */
pre {
overflow: auto;
overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
td.linenos pre {
padding: 5px 0px;
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
margin-left: 0.5em;
}
table.highlighttable td {
padding: 0 0.5em 0 0.5em;
}
tt.descname {
background-color: transparent;
font-weight: bold;
font-size: 1.2em;
}
tt.descclassname {
background-color: transparent;
}
tt.xref, a tt {
background-color: transparent;
font-weight: bold;
}
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
background-color: transparent;
}
.viewcode-link {
float: right;
}
.viewcode-back {
float: right;
font-family: sans-serif;
}
div.viewcode-block:target {
margin: -1px -10px;
padding: 0 10px;
}
/* -- math display ---------------------------------------------------------- */
img.math {
vertical-align: middle;
}
div.body div.math p {
text-align: center;
}
span.eqno {
float: right;
}
/* -- printout stylesheet --------------------------------------------------- */
@media print {
div.document,
div.documentwrapper,
div.bodywrapper {
margin: 0 !important;
width: 100%;
}
div.sphinxsidebar,
div.related,
div.footer,
#top-link {
display: none;
}
}

256
docs/_static/default.css vendored Normal file
View File

@@ -0,0 +1,256 @@
/*
* default.css_t
* ~~~~~~~~~~~~~
*
* Sphinx stylesheet -- default theme.
*
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: sans-serif;
font-size: 100%;
background-color: #11303d;
color: #000;
margin: 0;
padding: 0;
}
div.document {
background-color: #1c4e63;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 230px;
}
div.body {
background-color: #ffffff;
color: #000000;
padding: 0 20px 30px 20px;
}
div.footer {
color: #ffffff;
width: 100%;
padding: 9px 0 9px 0;
text-align: center;
font-size: 75%;
}
div.footer a {
color: #ffffff;
text-decoration: underline;
}
div.related {
background-color: #133f52;
line-height: 30px;
color: #ffffff;
}
div.related a {
color: #ffffff;
}
div.sphinxsidebar {
}
div.sphinxsidebar h3 {
font-family: 'Trebuchet MS', sans-serif;
color: #ffffff;
font-size: 1.4em;
font-weight: normal;
margin: 0;
padding: 0;
}
div.sphinxsidebar h3 a {
color: #ffffff;
}
div.sphinxsidebar h4 {
font-family: 'Trebuchet MS', sans-serif;
color: #ffffff;
font-size: 1.3em;
font-weight: normal;
margin: 5px 0 0 0;
padding: 0;
}
div.sphinxsidebar p {
color: #ffffff;
}
div.sphinxsidebar p.topless {
margin: 5px 10px 10px 10px;
}
div.sphinxsidebar ul {
margin: 10px;
padding: 0;
color: #ffffff;
}
div.sphinxsidebar a {
color: #98dbcc;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
/* -- hyperlink styles ------------------------------------------------------ */
a {
color: #355f7c;
text-decoration: none;
}
a:visited {
color: #355f7c;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* -- body styles ----------------------------------------------------------- */
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Trebuchet MS', sans-serif;
background-color: #f2f2f2;
font-weight: normal;
color: #20435c;
border-bottom: 1px solid #ccc;
margin: 20px -20px 10px -20px;
padding: 3px 0 3px 10px;
}
div.body h1 { margin-top: 0; font-size: 200%; }
div.body h2 { font-size: 160%; }
div.body h3 { font-size: 140%; }
div.body h4 { font-size: 120%; }
div.body h5 { font-size: 110%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #c60f0f;
font-size: 0.8em;
padding: 0 4px 0 4px;
text-decoration: none;
}
a.headerlink:hover {
background-color: #c60f0f;
color: white;
}
div.body p, div.body dd, div.body li {
text-align: justify;
line-height: 130%;
}
div.admonition p.admonition-title + p {
display: inline;
}
div.admonition p {
margin-bottom: 5px;
}
div.admonition pre {
margin-bottom: 5px;
}
div.admonition ul, div.admonition ol {
margin-bottom: 5px;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
div.warning {
background-color: #ffe4e4;
border: 1px solid #f66;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre {
padding: 5px;
background-color: #eeffcc;
color: #333333;
line-height: 120%;
border: 1px solid #ac9;
border-left: none;
border-right: none;
}
tt {
background-color: #ecf0f3;
padding: 0 1px 0 1px;
font-size: 0.95em;
}
th {
background-color: #ede;
}
.warning tt {
background: #efc2c2;
}
.note tt {
background: #d6d6d6;
}
.viewcode-back {
font-family: sans-serif;
}
div.viewcode-block:target {
background-color: #f4debf;
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
}

BIN
docs/_static/fonts/Museo_Slab_500.otf vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
docs/_static/fonts/OFLGoudyStMTT.ttf vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

431
docs/_static/haiku.css vendored Normal file
View File

@@ -0,0 +1,431 @@
/*
* haiku.css_t
* ~~~~~~~~~~~
*
* Sphinx stylesheet -- haiku theme.
*
* Adapted from http://haiku-os.org/docs/Haiku-doc.css.
* Original copyright message:
*
* Copyright 2008-2009, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Francois Revol <revol@free.fr>
* Stephan Assmus <superstippi@gmx.de>
* Braden Ewing <brewin@gmail.com>
* Humdinger <humdingerb@gmail.com>
*
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@import url("basic.css");
@font-face {
font-family: "Museo Slab";
font-weight: normal;
font-style: normal;
src: local("Museo Slab"),
url("fonts/Museo_Slab_500.otf") format("opentype");
}
@font-face {
font-family: "Yanone Kaffeesatz";
font-weight: bold;
font-style: normal;
src: local("Yanone Kaffeesatz"),
url("fonts/YanoneKaffeesatz-Bold.ttf") format("truetype");
}
@font-face {
font-family: "Yanone Kaffeesatz";
font-weight: lighter;
font-style: normal;
src: local("Yanone Kaffeesatz"),
url("fonts/YanoneKaffeesatz-Regular.ttf") format("truetype");
}
html {
margin: 0px;
padding: 0px;
background: #FFF url(header.png) top left repeat-x;
}
body {
line-height: 1.5;
margin: auto;
padding: 0px;
font-family: "Helvetica Neueu", Helvetica, sans-serif;
min-width: 30em;
max-width: 70em;
color: #444;
text-align: center;
}
div.footer {
padding: 8px;
font-size: 11px;
text-align: center;
letter-spacing: 0.5px;
}
/* link colors and text decoration */
a:link {
font-weight: bold;
text-decoration: none;
color: #00ADEE;
}
a:visited {
font-weight: bold;
text-decoration: none;
color: #00ADEE;
}
a:hover, a:active {
text-decoration: underline;
color: #F46DBA;
}
/* Some headers act as anchors, don't give them a hover effect */
h1 a:hover, a:active {
text-decoration: none;
color: #CFCFCF;
}
h2 a:hover, a:active {
text-decoration: none;
color: #CFCFCF;
}
h3 a:hover, a:active {
text-decoration: none;
color: #CFCFCF;
}
h4 a:hover, a:active {
text-decoration: none;
color: #CFCFCF;
}
a.headerlink {
color: #a7ce38;
padding-left: 5px;
}
a.headerlink:hover {
color: #a7ce38;
}
/* basic text elements */
div.content {
margin: auto;
margin-top: 20px;
margin-bottom: 50px;
font-size: 0.9em;
width: 700px;
text-align: left;
}
/* heading and navigation */
div.header {
position: relative;
margin: auto;
margin-top: 125px;
height: 85px;
padding: 0 40px;
font-family: "Yanone Kaffeesatz";
text-align: left;
width: 750px;
}
div.header h1 {
font-size: 2.6em;
font-weight: normal;
letter-spacing: 1px;
color: #CFCFCF;
border: 0;
margin: 0;
padding-top: 15px;
font-family: "Yanone Kaffeesatz";
text-shadow: 1px 1px 1px rgba(175, 175, 175, .8);
font-variant: small-caps;
}
div.header h1 a {
font-weight: normal;
color: #00ADEE;
}
div.header h2 {
font-size: 1.3em;
font-weight: normal;
letter-spacing: 1px;
text-transform: uppercase;
color: #aaa;
border: 0;
margin-top: -3px;
padding: 0;
font-family: "Yanone Kaffeesatz";
}
div.header img.rightlogo {
float: right;
}
div.title {
font-size: 1.3em;
font-weight: bold;
color: #CFCFCF;
border-bottom: dotted thin #e0e0e0;
margin-bottom: 25px;
}
div.topnav {
position: relative;
z-index: 0;
}
div.topnav p {
margin: auto;
margin-top: 0;
margin-bottom: 0px;
text-align: right;
font-size: 0.8em;
width: 750px;
}
div.bottomnav {
background: #eeeeee;
}
div.bottomnav p {
margin-right: 40px;
text-align: right;
font-size: 0.8em;
}
a.uplink {
font-weight: normal;
}
/* contents box */
table.index {
margin: 0px 0px 30px 30px;
padding: 1px;
border-width: 1px;
border-style: dotted;
border-color: #e0e0e0;
}
table.index tr.heading {
background-color: #e0e0e0;
text-align: center;
font-weight: bold;
font-size: 1.1em;
}
table.index tr.index {
background-color: #eeeeee;
}
table.index td {
padding: 5px 20px;
}
table.index a:link, table.index a:visited {
font-weight: normal;
text-decoration: none;
color: #4A7389;
}
table.index a:hover, table.index a:active {
text-decoration: underline;
color: #ff4500;
}
/* Haiku User Guide styles and layout */
/* Rounded corner boxes */
/* Common declarations */
div.admonition {
-webkit-border-radius: 10px;
-khtml-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
border-style: dotted;
border-width: thin;
border-color: #dcdcdc;
padding: 10px 15px 10px 15px;
margin-bottom: 15px;
margin-top: 15px;
}
div.note {
padding: 10px 15px 10px 15px;
background-color: #e4ffde;
/*background: #e4ffde url(alert_info_32.png) 15px 15px no-repeat;*/
min-height: 42px;
}
div.warning {
padding: 10px 15px 10px 15px;
background-color: #fffbc6;
/*background: #fffbc6 url(alert_warning_32.png) 15px 15px no-repeat;*/
min-height: 42px;
}
div.seealso {
background: #e4ffde;
}
/* More layout and styles */
h1 {
font-size: 1.6em;
color: #aaa;
border-bottom: dotted thin #e0e0e0;
margin-top: 30px;
font-family: "Museo Slab";
text-shadow: 1px 1px 1px rgba(175, 175, 175, .25);
}
h2 {
font-size: 1.5em;
font-weight: normal;
color: #aaa;
border-bottom: dotted thin #e0e0e0;
margin-top: 30px;
font-family: "Museo Slab";
text-shadow: 1px 1px 1px rgba(175, 175, 175, .25);
}
h3 {
font-size: 1.4em;
font-weight: normal;
color: #aaa;
margin-top: 30px;
font-family: "Museo Slab";
text-shadow: 1px 1px 1px rgba(175, 175, 175, .25);
}
h4 {
font-size: 1.3em;
font-weight: normal;
color: #CFCFCF;
margin-top: 30px;
}
p {
text-align: justify;
}
p.last {
margin-bottom: 0;
}
ol {
padding-left: 20px;
}
ul {
padding-left: 5px;
margin-top: 3px;
}
li {
line-height: 1.3;
}
div.content ul > li {
-moz-background-clip:border;
-moz-background-inline-policy:continuous;
-moz-background-origin:padding;
background: transparent url(bullet_orange.png) no-repeat scroll left 0.45em;
list-style-image: none;
list-style-type: none;
padding: 0 0 0 1.666em;
margin-bottom: 3px;
}
td {
vertical-align: top;
}
tt {
background-color: #e2e2e2;
font-size: 1.0em;
font-family: monospace;
}
pre {
font-size: 1.1em;
margin: 0 0 12px 0;
padding: 0.8em;
background-image: url(noise_dk.png);
background-color: #222;
}
hr {
border-top: 1px solid #ccc;
border-bottom: 0;
border-right: 0;
border-left: 0;
margin-bottom: 10px;
margin-top: 20px;
}
/* printer only pretty stuff */
@media print {
.noprint {
display: none;
}
/* for acronyms we want their definitions inlined at print time */
acronym[title]:after {
font-size: small;
content: " (" attr(title) ")";
font-style: italic;
}
/* and not have mozilla dotted underline */
acronym {
border: none;
}
div.topnav, div.bottomnav, div.header, table.index {
display: none;
}
div.content {
margin: 0px;
padding: 0px;
}
html {
background: #FFF;
}
}
.viewcode-back {
font-family: "DejaVu Sans", Arial, Helvetica, sans-serif;
}
div.viewcode-block:target {
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
margin: -1px -12px;
padding: 0 12px;
}
#from_andyet {
-webkit-box-shadow: #CCC 0px 0px 3px;
background: rgba(255, 255, 255, 1);
bottom: 0px;
right: 17px;
padding: 3px 10px;
position: fixed;
}
#from_andyet h2 {
background-image: url("images/from_&yet.png");
background-repeat: no-repeat;
height: 29px;
line-height: 0;
text-indent: -9999em;
width: 79px;
margin-top: 0;
margin: 0px;
padding: 0px;
}

BIN
docs/_static/header.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
docs/_static/images/arch_layers.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
docs/_static/images/from_&yet.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

70
docs/_static/ir_black.css vendored Normal file
View File

@@ -0,0 +1,70 @@
.highlight .hll { background-color: #ffffcc }
.highlight { background: #000000; color: #f6f3e8; }
.highlight .c { color: #7C7C7C; } /* Comment */
.highlight .err { color: #f6f3e8; } /* Error */
.highlight .g { color: #f6f3e8; } /* Generic */
.highlight .k { color: #00ADEE; } /* Keyword */
.highlight .l { color: #f6f3e8; } /* Literal */
.highlight .n { color: #f6f3e8; } /* Name */
.highlight .o { color: #f6f3e8; } /* Operator */
.highlight .x { color: #f6f3e8; } /* Other */
.highlight .p { color: #f6f3e8; } /* Punctuation */
.highlight .cm { color: #7C7C7C; } /* Comment.Multiline */
.highlight .cp { color: #96CBFE; } /* Comment.Preproc */
.highlight .c1 { color: #7C7C7C; } /* Comment.Single */
.highlight .cs { color: #7C7C7C; } /* Comment.Special */
.highlight .gd { color: #f6f3e8; } /* Generic.Deleted */
.highlight .ge { color: #f6f3e8; } /* Generic.Emph */
.highlight .gr { color: #ffffff; background-color: #ff0000 } /* Generic.Error */
.highlight .gh { color: #f6f3e8; font-weight: bold; } /* Generic.Heading */
.highlight .gi { color: #f6f3e8; } /* Generic.Inserted */
.highlight .go { color: #070707; } /* Generic.Output */
.highlight .gp { color: #f6f3e8; } /* Generic.Prompt */
.highlight .gs { color: #f6f3e8; } /* Generic.Strong */
.highlight .gu { color: #f6f3e8; font-weight: bold; } /* Generic.Subheading */
.highlight .gt { color: #ffffff; font-weight: bold; background-color: #FF6C60 } /* Generic.Traceback */
.highlight .kc { color: #6699CC; } /* Keyword.Constant */
.highlight .kd { color: #6699CC; } /* Keyword.Declaration */
.highlight .kn { color: #6699CC; } /* Keyword.Namespace */
.highlight .kp { color: #6699CC; } /* Keyword.Pseudo */
.highlight .kr { color: #6699CC; } /* Keyword.Reserved */
.highlight .kt { color: #FFFFB6; } /* Keyword.Type */
.highlight .ld { color: #f6f3e8; } /* Literal.Date */
.highlight .m { color: #FF73FD; } /* Literal.Number */
.highlight .s { color: #F46DBA;/*#A8FF60;*/ } /* Literal.String */
.highlight .na { color: #f6f3e8; } /* Name.Attribute */
.highlight .nb { color: #f6f3e8; } /* Name.Builtin */
.highlight .nc { color: #f6f3e8; } /* Name.Class */
.highlight .no { color: #99CC99; } /* Name.Constant */
.highlight .nd { color: #f6f3e8; } /* Name.Decorator */
.highlight .ni { color: #E18964; } /* Name.Entity */
.highlight .ne { color: #f6f3e8; } /* Name.Exception */
.highlight .nf { color: #F64DBA; } /* Name.Function */
.highlight .nl { color: #f6f3e8; } /* Name.Label */
.highlight .nn { color: #f6f3e8; } /* Name.Namespace */
.highlight .nx { color: #f6f3e8; } /* Name.Other */
.highlight .py { color: #f6f3e8; } /* Name.Property */
.highlight .nt { color: #00ADEE; } /* Name.Tag */
.highlight .nv { color: #C6C5FE; } /* Name.Variable */
.highlight .ow { color: #ffffff; } /* Operator.Word */
.highlight .w { color: #f6f3e8; } /* Text.Whitespace */
.highlight .mf { color: #FF73FD; } /* Literal.Number.Float */
.highlight .mh { color: #FF73FD; } /* Literal.Number.Hex */
.highlight .mi { color: #FF73FD; } /* Literal.Number.Integer */
.highlight .mo { color: #FF73FD; } /* Literal.Number.Oct */
.highlight .sb { color: #A8FF60; } /* Literal.String.Backtick */
.highlight .sc { color: #A8FF60; } /* Literal.String.Char */
.highlight .sd { color: #A8FF60; } /* Literal.String.Doc */
.highlight .s2 { color: #A8FF60; } /* Literal.String.Double */
.highlight .se { color: #A8FF60; } /* Literal.String.Escape */
.highlight .sh { color: #A8FF60; } /* Literal.String.Heredoc */
.highlight .si { color: #A8FF60; } /* Literal.String.Interpol */
.highlight .sx { color: #A8FF60; } /* Literal.String.Other */
.highlight .sr { color: #A8FF60; } /* Literal.String.Regex */
.highlight .s1 { color: #A8FF60; } /* Literal.String.Single */
.highlight .ss { color: #A8FF60; } /* Literal.String.Symbol */
.highlight .bp { color: #f6f3e8; } /* Name.Builtin.Pseudo */
.highlight .vc { color: #C6C5FE; } /* Name.Variable.Class */
.highlight .vg { color: #C6C5FE; } /* Name.Variable.Global */
.highlight .vi { color: #C6C5FE; } /* Name.Variable.Instance */
.highlight .il { color: #FF73FD; } /* Literal.Number.Integer.Long */

245
docs/_static/nature.css vendored Normal file
View File

@@ -0,0 +1,245 @@
/*
* nature.css_t
* ~~~~~~~~~~~~
*
* Sphinx stylesheet -- nature theme.
*
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: Arial, sans-serif;
font-size: 100%;
background-color: #111;
color: #555;
margin: 0;
padding: 0;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 230px;
}
hr {
border: 1px solid #B1B4B6;
}
div.document {
background-color: #eee;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 30px 30px;
font-size: 0.9em;
}
div.footer {
color: #555;
width: 100%;
padding: 13px 0;
text-align: center;
font-size: 75%;
}
div.footer a {
color: #444;
text-decoration: underline;
}
div.related {
background-color: #6BA81E;
line-height: 32px;
color: #fff;
text-shadow: 0px 1px 0 #444;
font-size: 0.9em;
}
div.related a {
color: #E2F3CC;
}
div.sphinxsidebar {
font-size: 0.75em;
line-height: 1.5em;
}
div.sphinxsidebarwrapper{
padding: 20px 0;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: Arial, sans-serif;
color: #222;
font-size: 1.2em;
font-weight: normal;
margin: 0;
padding: 5px 10px;
background-color: #ddd;
text-shadow: 1px 1px 0 white
}
div.sphinxsidebar h4{
font-size: 1.1em;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p {
color: #888;
padding: 5px 20px;
}
div.sphinxsidebar p.topless {
}
div.sphinxsidebar ul {
margin: 10px 20px;
padding: 0;
color: #000;
}
div.sphinxsidebar a {
color: #444;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: sans-serif;
font-size: 1em;
}
div.sphinxsidebar input[type=text]{
margin-left: 20px;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #005B81;
text-decoration: none;
}
a:hover {
color: #E32E00;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: Arial, sans-serif;
background-color: #BED4EB;
font-weight: normal;
color: #212224;
margin: 30px 0px 10px 0px;
padding: 5px 0 5px 10px;
text-shadow: 0px 1px 0 white
}
div.body h1 { border-top: 20px solid white; margin-top: 0; font-size: 200%; }
div.body h2 { font-size: 150%; background-color: #C8D5E3; }
div.body h3 { font-size: 120%; background-color: #D8DEE3; }
div.body h4 { font-size: 110%; background-color: #D8DEE3; }
div.body h5 { font-size: 100%; background-color: #D8DEE3; }
div.body h6 { font-size: 100%; background-color: #D8DEE3; }
a.headerlink {
color: #c60f0f;
font-size: 0.8em;
padding: 0 4px 0 4px;
text-decoration: none;
}
a.headerlink:hover {
background-color: #c60f0f;
color: white;
}
div.body p, div.body dd, div.body li {
line-height: 1.5em;
}
div.admonition p.admonition-title + p {
display: inline;
}
div.highlight{
background-color: white;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
div.warning {
background-color: #ffe4e4;
border: 1px solid #f66;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre {
padding: 10px;
background-color: White;
color: #222;
line-height: 1.2em;
border: 1px solid #C6C9CB;
font-size: 1.1em;
margin: 1.5em 0 1.5em 0;
-webkit-box-shadow: 1px 1px 1px #d8d8d8;
-moz-box-shadow: 1px 1px 1px #d8d8d8;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
font-size: 1.1em;
font-family: monospace;
}
.viewcode-back {
font-family: Arial, sans-serif;
}
div.viewcode-block:target {
background-color: #f4debf;
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
}

BIN
docs/_static/noise_dk.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

70
docs/_static/pygments.css vendored Normal file
View File

@@ -0,0 +1,70 @@
.highlight .hll { background-color: #ffffcc }
.highlight { background: #000000; color: #f6f3e8; }
.highlight .c { color: #7C7C7C; } /* Comment */
.highlight .err { color: #f6f3e8; } /* Error */
.highlight .g { color: #f6f3e8; } /* Generic */
.highlight .k { color: #00ADEE; } /* Keyword */
.highlight .l { color: #f6f3e8; } /* Literal */
.highlight .n { color: #f6f3e8; } /* Name */
.highlight .o { color: #f6f3e8; } /* Operator */
.highlight .x { color: #f6f3e8; } /* Other */
.highlight .p { color: #f6f3e8; } /* Punctuation */
.highlight .cm { color: #7C7C7C; } /* Comment.Multiline */
.highlight .cp { color: #96CBFE; } /* Comment.Preproc */
.highlight .c1 { color: #7C7C7C; } /* Comment.Single */
.highlight .cs { color: #7C7C7C; } /* Comment.Special */
.highlight .gd { color: #f6f3e8; } /* Generic.Deleted */
.highlight .ge { color: #f6f3e8; } /* Generic.Emph */
.highlight .gr { color: #ffffff; background-color: #ff0000 } /* Generic.Error */
.highlight .gh { color: #f6f3e8; font-weight: bold; } /* Generic.Heading */
.highlight .gi { color: #f6f3e8; } /* Generic.Inserted */
.highlight .go { color: #070707; } /* Generic.Output */
.highlight .gp { color: #f6f3e8; } /* Generic.Prompt */
.highlight .gs { color: #f6f3e8; } /* Generic.Strong */
.highlight .gu { color: #f6f3e8; font-weight: bold; } /* Generic.Subheading */
.highlight .gt { color: #ffffff; font-weight: bold; background-color: #FF6C60 } /* Generic.Traceback */
.highlight .kc { color: #6699CC; } /* Keyword.Constant */
.highlight .kd { color: #6699CC; } /* Keyword.Declaration */
.highlight .kn { color: #6699CC; } /* Keyword.Namespace */
.highlight .kp { color: #6699CC; } /* Keyword.Pseudo */
.highlight .kr { color: #6699CC; } /* Keyword.Reserved */
.highlight .kt { color: #FFFFB6; } /* Keyword.Type */
.highlight .ld { color: #f6f3e8; } /* Literal.Date */
.highlight .m { color: #FF73FD; } /* Literal.Number */
.highlight .s { color: #F46DBA;/*#A8FF60;*/ } /* Literal.String */
.highlight .na { color: #f6f3e8; } /* Name.Attribute */
.highlight .nb { color: #f6f3e8; } /* Name.Builtin */
.highlight .nc { color: #f6f3e8; } /* Name.Class */
.highlight .no { color: #99CC99; } /* Name.Constant */
.highlight .nd { color: #f6f3e8; } /* Name.Decorator */
.highlight .ni { color: #E18964; } /* Name.Entity */
.highlight .ne { color: #f6f3e8; } /* Name.Exception */
.highlight .nf { color: #F64DBA; } /* Name.Function */
.highlight .nl { color: #f6f3e8; } /* Name.Label */
.highlight .nn { color: #f6f3e8; } /* Name.Namespace */
.highlight .nx { color: #f6f3e8; } /* Name.Other */
.highlight .py { color: #f6f3e8; } /* Name.Property */
.highlight .nt { color: #00ADEE; } /* Name.Tag */
.highlight .nv { color: #C6C5FE; } /* Name.Variable */
.highlight .ow { color: #ffffff; } /* Operator.Word */
.highlight .w { color: #f6f3e8; } /* Text.Whitespace */
.highlight .mf { color: #FF73FD; } /* Literal.Number.Float */
.highlight .mh { color: #FF73FD; } /* Literal.Number.Hex */
.highlight .mi { color: #FF73FD; } /* Literal.Number.Integer */
.highlight .mo { color: #FF73FD; } /* Literal.Number.Oct */
.highlight .sb { color: #A8FF60; } /* Literal.String.Backtick */
.highlight .sc { color: #A8FF60; } /* Literal.String.Char */
.highlight .sd { color: #A8FF60; } /* Literal.String.Doc */
.highlight .s2 { color: #A8FF60; } /* Literal.String.Double */
.highlight .se { color: #A8FF60; } /* Literal.String.Escape */
.highlight .sh { color: #A8FF60; } /* Literal.String.Heredoc */
.highlight .si { color: #A8FF60; } /* Literal.String.Interpol */
.highlight .sx { color: #A8FF60; } /* Literal.String.Other */
.highlight .sr { color: #A8FF60; } /* Literal.String.Regex */
.highlight .s1 { color: #A8FF60; } /* Literal.String.Single */
.highlight .ss { color: #A8FF60; } /* Literal.String.Symbol */
.highlight .bp { color: #f6f3e8; } /* Name.Builtin.Pseudo */
.highlight .vc { color: #C6C5FE; } /* Name.Variable.Class */
.highlight .vg { color: #C6C5FE; } /* Name.Variable.Global */
.highlight .vi { color: #C6C5FE; } /* Name.Variable.Instance */
.highlight .il { color: #FF73FD; } /* Literal.Number.Integer.Long */

339
docs/_static/sphinxdoc.css vendored Normal file
View File

@@ -0,0 +1,339 @@
/*
* sphinxdoc.css_t
* ~~~~~~~~~~~~~~~
*
* Sphinx stylesheet -- sphinxdoc theme. Originally created by
* Armin Ronacher for Werkzeug.
*
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
'Verdana', sans-serif;
font-size: 14px;
letter-spacing: -0.01em;
line-height: 150%;
text-align: center;
background-color: #BFD1D4;
color: black;
padding: 0;
border: 1px solid #aaa;
margin: 0px 80px 0px 80px;
min-width: 740px;
}
div.document {
background-color: white;
text-align: left;
background-image: url(contents.png);
background-repeat: repeat-x;
}
div.bodywrapper {
margin: 0 240px 0 0;
border-right: 1px solid #ccc;
}
div.body {
margin: 0;
padding: 0.5em 20px 20px 20px;
}
div.related {
font-size: 1em;
}
div.related ul {
background-image: url(navigation.png);
height: 2em;
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
}
div.related ul li {
margin: 0;
padding: 0;
height: 2em;
float: left;
}
div.related ul li.right {
float: right;
margin-right: 5px;
}
div.related ul li a {
margin: 0;
padding: 0 5px 0 5px;
line-height: 1.75em;
color: #EE9816;
}
div.related ul li a:hover {
color: #3CA8E7;
}
div.sphinxsidebarwrapper {
padding: 0;
}
div.sphinxsidebar {
margin: 0;
padding: 0.5em 15px 15px 0;
width: 210px;
float: right;
font-size: 1em;
text-align: left;
}
div.sphinxsidebar h3, div.sphinxsidebar h4 {
margin: 1em 0 0.5em 0;
font-size: 1em;
padding: 0.1em 0 0.1em 0.5em;
color: white;
border: 1px solid #86989B;
background-color: #AFC1C4;
}
div.sphinxsidebar h3 a {
color: white;
}
div.sphinxsidebar ul {
padding-left: 1.5em;
margin-top: 7px;
padding: 0;
line-height: 130%;
}
div.sphinxsidebar ul ul {
margin-left: 20px;
}
div.footer {
background-color: #E3EFF1;
color: #86989B;
padding: 3px 8px 3px 0;
clear: both;
font-size: 0.8em;
text-align: right;
}
div.footer a {
color: #86989B;
text-decoration: underline;
}
/* -- body styles ----------------------------------------------------------- */
p {
margin: 0.8em 0 0.5em 0;
}
a {
color: #CA7900;
text-decoration: none;
}
a:hover {
color: #2491CF;
}
div.body a {
text-decoration: underline;
}
h1 {
margin: 0;
padding: 0.7em 0 0.3em 0;
font-size: 1.5em;
color: #11557C;
}
h2 {
margin: 1.3em 0 0.2em 0;
font-size: 1.35em;
padding: 0;
}
h3 {
margin: 1em 0 -0.3em 0;
font-size: 1.2em;
}
div.body h1 a, div.body h2 a, div.body h3 a, div.body h4 a, div.body h5 a, div.body h6 a {
color: black!important;
}
h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor {
display: none;
margin: 0 0 0 0.3em;
padding: 0 0.2em 0 0.2em;
color: #aaa!important;
}
h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor,
h5:hover a.anchor, h6:hover a.anchor {
display: inline;
}
h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover,
h5 a.anchor:hover, h6 a.anchor:hover {
color: #777;
background-color: #eee;
}
a.headerlink {
color: #c60f0f!important;
font-size: 1em;
margin-left: 6px;
padding: 0 4px 0 4px;
text-decoration: none!important;
}
a.headerlink:hover {
background-color: #ccc;
color: white!important;
}
cite, code, tt {
font-family: 'Consolas', 'Deja Vu Sans Mono',
'Bitstream Vera Sans Mono', monospace;
font-size: 0.95em;
letter-spacing: 0.01em;
}
tt {
background-color: #f2f2f2;
border-bottom: 1px solid #ddd;
color: #333;
}
tt.descname, tt.descclassname, tt.xref {
border: 0;
}
hr {
border: 1px solid #abc;
margin: 2em;
}
a tt {
border: 0;
color: #CA7900;
}
a tt:hover {
color: #2491CF;
}
pre {
font-family: 'Consolas', 'Deja Vu Sans Mono',
'Bitstream Vera Sans Mono', monospace;
font-size: 0.95em;
letter-spacing: 0.015em;
line-height: 120%;
padding: 0.5em;
border: 1px solid #ccc;
background-color: #f8f8f8;
}
pre a {
color: inherit;
text-decoration: underline;
}
td.linenos pre {
padding: 0.5em 0;
}
div.quotebar {
background-color: #f8f8f8;
max-width: 250px;
float: right;
padding: 2px 7px;
border: 1px solid #ccc;
}
div.topic {
background-color: #f8f8f8;
}
table {
border-collapse: collapse;
margin: 0 -0.5em 0 -0.5em;
}
table td, table th {
padding: 0.2em 0.5em 0.2em 0.5em;
}
div.admonition, div.warning {
font-size: 0.9em;
margin: 1em 0 1em 0;
border: 1px solid #86989B;
background-color: #f7f7f7;
padding: 0;
}
div.admonition p, div.warning p {
margin: 0.5em 1em 0.5em 1em;
padding: 0;
}
div.admonition pre, div.warning pre {
margin: 0.4em 1em 0.4em 1em;
}
div.admonition p.admonition-title,
div.warning p.admonition-title {
margin: 0;
padding: 0.1em 0 0.1em 0.5em;
color: white;
border-bottom: 1px solid #86989B;
font-weight: bold;
background-color: #AFC1C4;
}
div.warning {
border: 1px solid #940000;
}
div.warning p.admonition-title {
background-color: #CF0000;
border-bottom-color: #940000;
}
div.admonition ul, div.admonition ol,
div.warning ul, div.warning ol {
margin: 0.1em 0.5em 0.5em 3em;
padding: 0;
}
div.versioninfo {
margin: 1em 0 0 0;
border: 1px solid #ccc;
background-color: #DDEAF0;
padding: 8px;
line-height: 1.3em;
font-size: 0.9em;
}
.viewcode-back {
font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
'Verdana', sans-serif;
}
div.viewcode-block:target {
background-color: #f4debf;
border-top: 1px solid #ac9;
border-bottom: 1px solid #ac9;
}

70
docs/_templates/layout.html vendored Normal file
View File

@@ -0,0 +1,70 @@
{#
haiku/layout.html
~~~~~~~~~~~~~~~~~
Sphinx layout template for the haiku theme.
:copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
#}
{% extends "basic/layout.html" %}
{% set script_files = script_files + ['_static/theme_extras.js'] %}
{% set css_files = css_files + ['_static/print.css'] %}
{# do not display relbars #}
{% block relbar1 %}{% endblock %}
{% block relbar2 %}{% endblock %}
{% macro nav() %}
<p>
{%- block haikurel1 %}
{%- endblock %}
{%- if prev %}
«&#160;&#160;<a href="{{ prev.link|e }}">{{ prev.title }}</a>
&#160;&#160;::&#160;&#160;
{%- endif %}
<a class="uplink" href="{{ pathto(master_doc) }}">{{ _('Contents') }}</a>
{%- if next %}
&#160;&#160;::&#160;&#160;
<a href="{{ next.link|e }}">{{ next.title }}</a>&#160;&#160;»
{%- endif %}
{%- block haikurel2 %}
{%- endblock %}
</p>
{% endmacro %}
{% block content %}
<div class="header">
{%- block haikuheader %}
{%- if theme_full_logo != "false" %}
<a href="{{ pathto('index') }}">
<img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
</a>
{%- else %}
{%- if logo -%}
<img class="rightlogo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
{%- endif -%}
<h1 class="heading"><a href="{{ pathto('index') }}">
<span>{{ title|striptags }}</span></a></h1>
<h2 class="heading"><span>{{ shorttitle|e }}</span></h2>
{%- endif %}
{%- endblock %}
</div>
<div class="topnav">
{{ nav() }}
</div>
<div class="content">
{#{%- if display_toc %}
<div id="toc">
<h3>Table Of Contents</h3>
{{ toc }}
</div>
{%- endif %}#}
{% block body %}{% endblock %}
</div>
<div class="bottomnav">
{{ nav() }}
</div>
<a id="from_andyet" href="http://andyet.net"><h2>From &amp;yet</h2></a>
{% endblock %}

8
docs/api/basexmpp.rst Normal file
View File

@@ -0,0 +1,8 @@
========
BaseXMPP
========
.. module:: sleekxmpp.basexmpp
.. autoclass:: BaseXMPP
:members:

8
docs/api/clientxmpp.rst Normal file
View File

@@ -0,0 +1,8 @@
==========
ClientXMPP
==========
.. module:: sleekxmpp.clientxmpp
.. autoclass:: ClientXMPP
:members:

View File

@@ -0,0 +1,8 @@
=============
ComponentXMPP
=============
.. module:: sleekxmpp.componentxmpp
.. autoclass:: ComponentXMPP
:members:

14
docs/api/exceptions.rst Normal file
View File

@@ -0,0 +1,14 @@
Exceptions
==========
.. module:: sleekxmpp.exceptions
.. autoexception:: XMPPError
:members:
.. autoexception:: IqError
:members:
.. autoexception:: IqTimeout
:members:

View File

@@ -0,0 +1,12 @@
.. module:: sleekxmpp.xmlstream.filesocket
.. _filesocket:
Python 2.6 File Socket Shims
============================
.. autoclass:: FileSocket
:members:
.. autoclass:: Socket26
:members:

View File

@@ -0,0 +1,24 @@
Stanza Handlers
===============
The Basic Handler
-----------------
.. module:: sleekxmpp.xmlstream.handler.base
.. autoclass:: BaseHandler
:members:
Callback
--------
.. module:: sleekxmpp.xmlstream.handler.callback
.. autoclass:: Callback
:members:
Waiter
------
.. module:: sleekxmpp.xmlstream.handler.waiter
.. autoclass:: Waiter
:members:

View File

@@ -0,0 +1,7 @@
Jabber IDs (JID)
=================
.. module:: sleekxmpp.xmlstream.jid
.. autoclass:: JID
:members:

View File

@@ -0,0 +1,41 @@
Stanza Matchers
===============
The Basic Matcher
-----------------
.. module:: sleekxmpp.xmlstream.matcher.base
.. autoclass:: MatcherBase
:members:
ID Matching
-----------
.. module:: sleekxmpp.xmlstream.matcher.id
.. autoclass:: MatcherId
:members:
Stanza Path Matching
--------------------
.. module:: sleekxmpp.xmlstream.matcher.stanzapath
.. autoclass:: StanzaPath
:members:
XPath
-----
.. module:: sleekxmpp.xmlstream.matcher.xpath
.. autoclass:: MatchXPath
:members:
XMLMask
-------
.. module:: sleekxmpp.xmlstream.matcher.xmlmask
.. autoclass:: MatchXMLMask
:members:

View File

@@ -0,0 +1,11 @@
=========
Scheduler
=========
.. module:: sleekxmpp.xmlstream.scheduler
.. autoclass:: Task
:members:
.. autoclass:: Scheduler
:members:

View File

@@ -0,0 +1,123 @@
.. _stanzabase:
==============
Stanza Objects
==============
.. module:: sleekxmpp.xmlstream.stanzabase
The :mod:`~sleekmxpp.xmlstream.stanzabase` module provides a wrapper for the
standard :mod:`~xml.etree.ElementTree` module that makes working with XML
less painful. Instead of having to manually move up and down an element
tree and insert subelements and attributes, you can interact with an object
that behaves like a normal dictionary or JSON object, which silently maps
keys to XML attributes and elements behind the scenes.
Overview
--------
The usefulness of this layer grows as the XML you have to work with
becomes nested. The base unit here, :class:`ElementBase`, can map to a
single XML element, or several depending on how advanced of a mapping
is desired from interface keys to XML structures. For example, a single
:class:`ElementBase` derived class could easily describe:
.. code-block:: xml
<message to="user@example.com" from="friend@example.com">
<body>Hi!</body>
<x:extra>
<x:item>Custom item 1</x:item>
<x:item>Custom item 2</x:item>
<x:item>Custom item 3</x:item>
</x:extra>
</message>
If that chunk of XML were put in the :class:`ElementBase` instance
``msg``, we could extract the data from the XML using::
>>> msg['extra']
['Custom item 1', 'Custom item 2', 'Custom item 3']
Provided we set up the handler for the ``'extra'`` interface to load the
``<x:item>`` element content into a list.
The key concept is that given an XML structure that will be repeatedly
used, we can define a set of :term:`interfaces` which when we read from,
write to, or delete, will automatically manipulate the underlying XML
as needed. In addition, some of these interfaces may in turn reference
child objects which expose interfaces for particularly complex child
elements of the original XML chunk.
.. seealso::
:ref:`create-stanza-interfaces`.
Because the :mod:`~sleekxmpp.xmlstream.stanzabase` module was developed
as part of an `XMPP <http://xmpp.org>`_ library, these chunks of XML are
referred to as :term:`stanzas <stanza>`, and in SleekXMPP we refer to a
subclass of :class:`ElementBase` which defines the interfaces needed for
interacting with a given :term:`stanza` a :term:`stanza object`.
To make dealing with more complicated and nested :term:`stanzas <stanza>`
or XML chunks easier, :term:`stanza objects <stanza object>` can be
composed in two ways: as iterable child objects or as plugins. Iterable
child stanzas, or :term:`substanzas`, are accessible through a special
``'substanzas'`` interface. This option is useful for stanzas which
may contain more than one of the same kind of element. When there is
only one child element, the plugin method is more useful. For plugins,
a parent stanza object delegates one of its XML child elements to the
plugin stanza object. Here is an example:
.. code-block:: xml
<iq type="result">
<query xmlns="http://jabber.org/protocol/disco#info">
<identity category="client" type="bot" name="SleekXMPP Bot" />
</query>
</iq>
We can can arrange this stanza into two objects: an outer, wrapper object for
dealing with the ``<iq />`` element and its attributes, and a plugin object to
control the ``<query />`` payload element. If we give the plugin object the
name ``'disco_info'`` (using its :attr:`ElementBase.plugin_attrib` value), then
we can access the plugin as so::
>>> iq['disco_info']
'<query xmlns="http://jabber.org/protocol/disco#info">
<identity category="client" type="bot" name="SleekXMPP Bot" />
</query>'
We can then drill down through the plugin object's interfaces as desired::
>>> iq['disco_info']['identities']
[('client', 'bot', 'SleekXMPP Bot')]
Plugins may also add new interfaces to the parent stanza object as if they
had been defined by the parent directly, and can also override the behaviour
of an interface defined by the parent.
.. seealso::
- :ref:`create-stanza-plugins`
- :ref:`create-extension-plugins`
- :ref:`override-parent-interfaces`
Registering Stanza Plugins
--------------------------
.. autofunction:: register_stanza_plugin
ElementBase
-----------
.. autoclass:: ElementBase
:members:
:private-members:
:special-members:
StanzaBase
----------
.. autoclass:: StanzaBase
:members:

View File

@@ -0,0 +1,46 @@
.. module:: sleekxmpp.xmlstream.tostring
.. _tostring:
XML Serialization
=================
Since the XML layer of SleekXMPP is based on :mod:`~xml.etree.ElementTree`,
why not just use the built-in :func:`~xml.etree.ElementTree.tostring`
method? The answer is that using that method produces ugly results when
using namespaces. The :func:`tostring()` method used here intelligently
hides namespaces when able and does not introduce excessive namespace
prefixes::
>>> from sleekxmpp.xmlstream.tostring import tostring
>>> from xml.etree import cElementTree as ET
>>> xml = ET.fromstring('<foo xmlns="bar"><baz /></foo>')
>>> ET.tostring(xml)
'<ns0:foo xmlns:ns0="bar"><ns0:baz /></foo>'
>>> tostring(xml)
'<foo xmlns="bar"><baz /></foo>'
As a side effect of this namespace hiding, using :func:`tostring()` may
produce unexpected results depending on how the :func:`tostring()` method
is invoked. For example, when sending XML on the wire, the main XMPP
stanzas with their namespace of ``jabber:client`` will not include the
namespace because that is already declared by the stream header. But, if
you create a :class:`~sleekxmpp.stanza.message.Message` instance and dump
it to the terminal, the ``jabber:client`` namespace will appear.
.. autofunction:: tostring
Escaping Special Characters
---------------------------
In order to prevent errors when sending arbitrary text as the textual
content of an XML element, certain characters must be escaped. These
are: ``&``, ``<``, ``>``, ``"``, and ``'``. The default escaping
mechanism is to replace those characters with their equivalent escape
entities: ``&amp;``, ``&lt;``, ``&gt;``, ``&apos;``, and ``&quot;``.
In the future, the use of CDATA sections may be allowed to reduce the
size of escaped text or for when other XMPP processing agents do not
undertand these entities.
.. autofunction:: xml_escape

View File

@@ -0,0 +1,10 @@
==========
XML Stream
==========
.. module:: sleekxmpp.xmlstream.xmlstream
.. autoexception:: RestartStream
.. autoclass:: XMLStream
:members:

177
docs/architecture.rst Normal file
View File

@@ -0,0 +1,177 @@
.. index:: XMLStream, BaseXMPP, ClientXMPP, ComponentXMPP
SleekXMPP Architecture
======================
The core of SleekXMPP is contained in four classes: ``XMLStream``,
``BaseXMPP``, ``ClientXMPP``, and ``ComponentXMPP``. Along side this
stack is a library for working with XML objects that eliminates most
of the tedium of creating/manipulating XML.
.. image:: _static/images/arch_layers.png
:height: 300px
:align: center
.. index:: XMLStream
The Foundation: XMLStream
-------------------------
:class:`~sleekxmpp.xmlstream.xmlstream.XMLStream` is a mostly XMPP-agnostic
class whose purpose is to read and write from a bi-directional XML stream.
It also allows for callback functions to execute when XML matching given
patterns is received; these callbacks are also referred to as :term:`stream
handlers <stream handler>`. The class also provides a basic eventing system
which can be triggered either manually or on a timed schedule.
The Main Threads
~~~~~~~~~~~~~~~~
:class:`~sleekxmpp.xmlstream.xmlstream.XMLStream` instances run using at
least three background threads: the send thread, the read thread, and the
scheduler thread. The send thread is in charge of monitoring the send queue
and writing text to the outgoing XML stream. The read thread pulls text off
of the incoming XML stream and stores the results in an event queue. The
scheduler thread is used to emit events after a given period of time.
Additionally, the main event processing loop may be executed in its
own thread if SleekXMPP is being used in the background for another
application.
Short-lived threads may also be spawned as requested for threaded
:term:`event handlers <event handler>`.
How XML Text is Turned into Action
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To demonstrate the flow of information, let's consider what happens
when this bit of XML is received (with an assumed namespace of
``jabber:client``):
.. code-block:: xml
<message to="user@example.com" from="friend@example.net">
<body>Hej!</body>
</message>
1. **Convert XML strings into objects.**
Incoming text is parsed and converted into XML objects (using
ElementTree) which are then wrapped into what are referred to as
:term:`Stanza objects <stanza object>`. The appropriate class for the
new object is determined using a map of namespaced element names to
classes.
Our incoming XML is thus turned into a :class:`~sleekxmpp.stanza.Message`
:term:`stanza object` because the namespaced element name
``{jabber:client}message`` is associated with the class
:class:`~sleekxmpp.stanza.Message`.
2. **Match stanza objects to callbacks.**
These objects are then compared against the stored patterns associated
with the registered callback handlers. For each match, a copy of the
:term:`stanza object` is paired with a reference to the handler and
placed into the event queue.
Our :class:`~sleekxmpp.stanza.Message` object is thus paired with the message stanza handler
:meth:`BaseXMPP._handle_message` to create the tuple::
('stanza', stanza_obj, handler)
3. **Process the event queue.**
The event queue is the heart of SleekXMPP. Nearly every action that
takes place is first inserted into this queue, whether that be received
stanzas, custom events, or scheduled events.
When the stanza is pulled out of the event queue with an associated
callback, the callback function is executed with the stanza as its only
parameter.
.. warning::
The callback, aka :term:`stream handler`, is executed in the main event
processing thread. If the handler blocks, event processing will also
block.
4. **Raise Custom Events**
Since a :term:`stream handler` shouldn't block, if extensive processing
for a stanza is required (such as needing to send and receive an
:class:`~sleekxmpp.stanza.Iq` stanza), then custom events must be used.
These events are not explicitly tied to the incoming XML stream and may
be raised at any time. Importantly, these events may be handled in their
own thread.
When the event is raised, a copy of the stanza is created for each
handler registered for the event. In contrast to :term:`stream handlers
<stream handler>`, these functions are referred to as :term:`event
handlers <event handler>`. Each stanza/handler pair is then put into the
event queue.
.. note::
It is possible to skip the event queue and process an event immediately
by using ``direct=True`` when raising the event.
The code for :meth:`BaseXMPP._handle_message` follows this pattern, and
raises a ``'message'`` event::
self.event('message', msg)
The event call then places the message object back into the event queue
paired with an :term:`event handler`::
('event', 'message', msg_copy1, custom_event_handler_1)
('event', 'message', msg_copy2, custom_evetn_handler_2)
5. **Process Custom Events**
The stanza and :term:`event handler` are then pulled from the event
queue, and the handler is executed, passing the stanza as its only
argument. If the handler was registered as threaded, then a new thread
will be spawned for it.
.. note::
Events may be raised without needing :term:`stanza objects <stanza object>`.
For example, you could use ``self.event('custom', {'a': 'b'})``.
You don't even need any arguments: ``self.event('no_parameters')``.
However, every event handler MUST accept at least one argument.
Finally, after a long trek, our message is handed off to the user's
custom handler in order to do awesome stuff::
msg.reply()
msg['body'] = "Hey! This is awesome!"
msg.send()
.. index:: BaseXMPP, XMLStream
Raising XMPP Awareness: BaseXMPP
--------------------------------
While :class:`~sleekxmpp.xmlstream.xmlstream.XMLStream` attempts to shy away
from anything too XMPP specific, :class:`~sleekxmpp.basexmpp.BaseXMPP`'s
sole purpose is to provide foundational support for sending and receiving
XMPP stanzas. This support includes registering the basic message,
presence, and iq stanzas, methods for creating and sending stanzas, and
default handlers for incoming messages and keeping track of presence
notifications.
The plugin system for adding new XEP support is also maintained by
:class:`~sleekxmpp.basexmpp.BaseXMPP`.
.. index:: ClientXMPP, BaseXMPP
ClientXMPP
----------
:class:`~sleekxmpp.clientxmpp.ClientXMPP` extends
:class:`~sleekxmpp.clientxmpp.BaseXMPP` with additional logic for connecting
to an XMPP server by performing DNS lookups. It also adds support for stream
features such as STARTTLS and SASL.
.. index:: ComponentXMPP, BaseXMPP
ComponentXMPP
-------------
:class:`~sleekxmpp.componentxmpp.ComponentXMPP` is only a thin layer on top of
:class:`~sleekxmpp.basexmpp.BaseXMPP` that implements the component handshake
protocol.

222
docs/conf.py Normal file
View File

@@ -0,0 +1,222 @@
# -*- coding: utf-8 -*-
#
# SleekXMPP documentation build configuration file, created by
# sphinx-quickstart on Tue Aug 9 22:27:06 2011.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.intersphinx']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'SleekXMPP'
copyright = u'2011, Nathan Fritz, Lance Stout'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.0'
# The full version, including alpha/beta/rc tags.
release = '1.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'tango'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'haiku'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {'headingcolor': '#CFCFCF', 'linkcolor': '#4A7389'}
# 00ADEE
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
html_title = 'SleekXMPP'
# A shorter title for the navigation bar. Default is the same as html_title.
html_short_title = '%s Documentation' % release
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
html_additional_pages = {
}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'SleekXMPPdoc'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'SleekXMPP.tex', u'SleekXMPP Documentation',
u'Nathan Fritz, Lance Stout', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'sleekxmpp', u'SleekXMPP Documentation',
[u'Nathan Fritz, Lance Stout'], 1)
]
intersphinx_mapping = {'python': ('http://docs.python.org/3.2', 'python-objects.inv')}

679
docs/create_plugin.rst Normal file
View File

@@ -0,0 +1,679 @@
.. _create-plugin:
Creating a SleekXMPP Plugin
===========================
One of the goals of SleekXMPP is to provide support for every draft or final
XMPP extension (`XEP <http://xmpp.org/extensions/>`_). To do this, SleekXMPP has a
plugin mechanism for adding the functionalities required by each XEP. But even
though plugins were made to quickly implement and prototype the official XMPP
extensions, there is no reason you can't create your own plugin to implement
your own custom XMPP-based protocol.
This guide will help walk you through the steps to
implement a rudimentary version of `XEP-0077 In-band
Registration <http://xmpp.org/extensions/xep-0077.html>`_. In-band registration
was implemented in example 14-6 (page 223) of `XMPP: The Definitive
Guide <http://oreilly.com/catalog/9780596521271>`_ because there was no SleekXMPP
plugin for XEP-0077 at the time of writing. We will partially fix that issue
here by turning the example implementation from *XMPP: The Definitive Guide*
into a plugin. Again, note that this will not a complete implementation, and a
different, more robust, official plugin for XEP-0077 may be added to SleekXMPP
in the future.
.. note::
The example plugin created in this guide is for the server side of the
registration process only. It will **NOT** be able to register new accounts
on an XMPP server.
First Steps
-----------
Every plugin inherits from the class :mod:`base_plugin <sleekxmpp.plugins.base.base_plugin>`,
and must include a ``plugin_init`` method. While the
plugins distributed with SleekXMPP must be placed in the plugins directory
``sleekxmpp/plugins`` to be loaded, custom plugins may be loaded from any
module. To do so, use the following form when registering the plugin:
.. code-block:: python
self.register_plugin('myplugin', module=mod_containing_my_plugin)
The plugin name must be the same as the plugin's class name.
Now, we can open our favorite text editors and create ``xep_0077.py`` in
``SleekXMPP/sleekxmpp/plugins``. We want to do some basic house-keeping and
declare the name and description of the XEP we are implementing. If you
are creating your own custom plugin, you don't need to include the ``xep``
attribute.
.. code-block:: python
"""
Creating a SleekXMPP Plugin
This is a minimal implementation of XEP-0077 to serve
as a tutorial for creating SleekXMPP plugins.
"""
from sleekxmpp.plugins.base import base_plugin
class xep_0077(base_plugin):
"""
XEP-0077 In-Band Registration
"""
def plugin_init(self):
self.description = "In-Band Registration"
self.xep = "0077"
Now that we have a basic plugin, we need to edit
``sleekxmpp/plugins/__init__.py`` to include our new plugin by adding
``'xep_0077'`` to the ``__all__`` declaration.
Interacting with Other Plugins
------------------------------
In-band registration is a feature that should be advertised through `Service
Discovery <http://xmpp.org/extensions/xep-0030.html>`_. To do that, we tell the
``xep_0030`` plugin to add the ``"jabber:iq:register"`` feature. We put this
call in a method named ``post_init`` which will be called once the plugin has
been loaded; by doing so we advertise that we can do registrations only after we
finish activating the plugin.
The ``post_init`` method needs to call ``base_plugin.post_init(self)``
which will mark that ``post_init`` has been called for the plugin. Once the
SleekXMPP object begins processing, ``post_init`` will be called on any plugins
that have not already run ``post_init``. This allows you to register plugins and
their dependencies without needing to worry about the order in which you do so.
**Note:** by adding this call we have introduced a dependency on the XEP-0030
plugin. Be sure to register ``'xep_0030'`` as well as ``'xep_0077'``. SleekXMPP
does not automatically load plugin dependencies for you.
.. code-block:: python
def post_init(self):
base_plugin.post_init(self)
self.xmpp['xep_0030'].add_feature("jabber:iq:register")
Creating Custom Stanza Objects
------------------------------
Now, the IQ stanzas needed to implement our version of XEP-0077 are not very
complex, and we could just interact with the XML objects directly just like
in the *XMPP: The Definitive Guide* example. However, creating custom stanza
objects is good practice.
We will create a new ``Registration`` stanza. Following the *XMPP: The
Definitive Guide* example, we will add support for a username and password
field. We also need two flags: ``registered`` and ``remove``. The ``registered``
flag is sent when an already registered user attempts to register, along with
their registration data. The ``remove`` flag is a request to unregister a user's
account.
Adding additional `fields specified in
XEP-0077 <http://xmpp.org/extensions/xep-0077.html#registrar-formtypes-register>`_
will not be difficult and is left as an exercise for the reader.
Our ``Registration`` class needs to start with a few descriptions of its
behaviour:
* ``namespace``
The namespace our stanza object lives in. In this case,
``"jabber:iq:register"``.
* ``name``
The name of the root XML element. In this case, the ``query`` element.
* ``plugin_attrib``
The name to access this type of stanza. In particular, given a
registration stanza, the ``Registration`` object can be found using:
``iq_object['register']``.
* ``interfaces``
A list of dictionary-like keys that can be used with the stanza object.
When using ``"key"``, if there exists a method of the form ``getKey``,
``setKey``, or``delKey`` (depending on context) then the result of calling
that method will be returned. Otherwise, the value of the attribute ``key``
of the main stanza element is returned if one exists.
**Note:** The accessor methods currently use title case, and not camel case.
Thus if you need to access an item named ``"methodName"`` you will need to
use ``getMethodname``. This naming convention might change to full camel
case in a future version of SleekXMPP.
* ``sub_interfaces``
A subset of ``interfaces``, but these keys map to the text of any
subelements that are direct children of the main stanza element. Thus,
referencing ``iq_object['register']['username']`` will either execute
``getUsername`` or return the value in the ``username`` element of the
query.
If you need to access an element, say ``elem``, that is not a direct child
of the main stanza element, you will need to add ``getElem``, ``setElem``,
and ``delElem``. See the note above about naming conventions.
.. code-block:: python
from sleekxmpp.xmlstream import ElementBase, ET, JID, register_stanza_plugin
from sleekxmpp import Iq
class Registration(ElementBase):
namespace = 'jabber:iq:register'
name = 'query'
plugin_attrib = 'register'
interfaces = set(('username', 'password', 'registered', 'remove'))
sub_interfaces = interfaces
def getRegistered(self):
present = self.xml.find('{%s}registered' % self.namespace)
return present is not None
def getRemove(self):
present = self.xml.find('{%s}remove' % self.namespace)
return present is not None
def setRegistered(self, registered):
if registered:
self.addField('registered')
else:
del self['registered']
def setRemove(self, remove):
if remove:
self.addField('remove')
else:
del self['remove']
def addField(self, name):
itemXML = ET.Element('{%s}%s' % (self.namespace, name))
self.xml.append(itemXML)
Setting a ``sub_interface`` attribute to ``""`` will remove that subelement.
Since we want to include empty registration fields in our form, we need the
``addField`` method to add the empty elements.
Since the ``registered`` and ``remove`` elements are just flags, we need to add
custom logic to enforce the binary behavior.
Extracting Stanzas from the XML Stream
--------------------------------------
Now that we have a custom stanza object, we need to be able to detect when we
receive one. To do this, we register a stream handler that will pattern match
stanzas off of the XML stream against our stanza object's element name and
namespace. To do so, we need to create a ``Callback`` object which contains
an XML fragment that can identify our stanza type. We can add this handler
registration to our ``plugin_init`` method.
Also, we need to associate our ``Registration`` class with IQ stanzas;
that requires the use of the ``register_stanza_plugin`` function (in
``sleekxmpp.xmlstream.stanzabase``) which takes the class of a parent stanza
type followed by the substanza type. In our case, the parent stanza is an IQ
stanza, and the substanza is our registration query.
The ``__handleRegistration`` method referenced in the callback will be our
handler function to process registration requests.
.. code-block:: python
def plugin_init(self):
self.description = "In-Band Registration"
self.xep = "0077"
self.xmpp.registerHandler(
Callback('In-Band Registration',
MatchXPath('{%s}iq/{jabber:iq:register}query' % self.xmpp.default_ns),
self.__handleRegistration))
register_stanza_plugin(Iq, Registration)
Handling Incoming Stanzas and Triggering Events
-----------------------------------------------
There are six situations that we need to handle to finish our implementation of
XEP-0077.
**Registration Form Request from a New User:**
.. code-block:: xml
<iq type="result">
<query xmlns="jabber:iq:register">
<username />
<password />
</query>
</iq>
**Registration Form Request from an Existing User:**
.. code-block:: xml
<iq type="result">
<query xmlns="jabber:iq:register">
<registered />
<username>Foo</username>
<password>hunter2</password>
</query>
</iq>
**Unregister Account:**
.. code-block:: xml
<iq type="result">
<query xmlns="jabber:iq:register" />
</iq>
**Incomplete Registration:**
.. code-block:: xml
<iq type="error">
<query xmlns="jabber:iq:register">
<username>Foo</username>
</query>
<error code="406" type="modify">
<not-acceptable xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
</error>
</iq>
**Conflicting Registrations:**
.. code-block:: xml
<iq type="error">
<query xmlns="jabber:iq:register">
<username>Foo</username>
<password>hunter2</password>
</query>
<error code="409" type="cancel">
<conflict xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
</error>
</iq>
**Successful Registration:**
.. code-block:: xml
<iq type="result">
<query xmlns="jabber:iq:register" />
</iq>
Cases 1 and 2: Registration Requests
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Responding to registration requests depends on if the requesting user already
has an account. If there is an account, the response should include the
``registered`` flag and the user's current registration information. Otherwise,
we just send the fields for our registration form.
We will handle both cases by creating a ``sendRegistrationForm`` method that
will create either an empty of full form depending on if we provide it with
user data. Since we need to know which form fields to include (especially if we
add support for the other fields specified in XEP-0077), we will also create a
method ``setForm`` which will take the names of the fields we wish to include.
.. code-block:: python
def plugin_init(self):
self.description = "In-Band Registration"
self.xep = "0077"
self.form_fields = ('username', 'password')
... remainder of plugin_init
...
def __handleRegistration(self, iq):
if iq['type'] == 'get':
# Registration form requested
userData = self.backend[iq['from'].bare]
self.sendRegistrationForm(iq, userData)
def setForm(self, *fields):
self.form_fields = fields
def sendRegistrationForm(self, iq, userData=None):
reg = iq['register']
if userData is None:
userData = {}
else:
reg['registered'] = True
for field in self.form_fields:
data = userData.get(field, '')
if data:
# Add field with existing data
reg[field] = data
else:
# Add a blank field
reg.addField(field)
iq.reply().setPayload(reg.xml)
iq.send()
Note how we are able to access our ``Registration`` stanza object with
``iq['register']``.
A User Backend
++++++++++++++
You might have noticed the reference to ``self.backend``, which is an object
that abstracts away storing and retrieving user information. Since it is not
much more than a dictionary, we will leave the implementation details to the
final, full source code example.
Case 3: Unregister an Account
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The next simplest case to consider is responding to a request to remove
an account. If we receive a ``remove`` flag, we instruct the backend to
remove the user's account. Since your application may need to know about
when users are registered or unregistered, we trigger an event using
``self.xmpp.event('unregister_user', iq)``. See the component examples below for
how to respond to that event.
.. code-block:: python
def __handleRegistration(self, iq):
if iq['type'] == 'get':
# Registration form requested
userData = self.backend[iq['from'].bare]
self.sendRegistrationForm(iq, userData)
elif iq['type'] == 'set':
# Remove an account
if iq['register']['remove']:
self.backend.unregister(iq['from'].bare)
self.xmpp.event('unregistered_user', iq)
iq.reply().send()
return
Case 4: Incomplete Registration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For the next case we need to check the user's registration to ensure it has all
of the fields we wanted. The simple option that we will use is to loop over the
field names and check each one; however, this means that all fields we send to
the user are required. Adding optional fields is left to the reader.
Since we have received an incomplete form, we need to send an error message back
to the user. We have to send a few different types of errors, so we will also
create a ``_sendError`` method that will add the appropriate ``error`` element
to the IQ reply.
.. code-block:: python
def __handleRegistration(self, iq):
if iq['type'] == 'get':
# Registration form requested
userData = self.backend[iq['from'].bare]
self.sendRegistrationForm(iq, userData)
elif iq['type'] == 'set':
if iq['register']['remove']:
# Remove an account
self.backend.unregister(iq['from'].bare)
self.xmpp.event('unregistered_user', iq)
iq.reply().send()
return
for field in self.form_fields:
if not iq['register'][field]:
# Incomplete Registration
self._sendError(iq, '406', 'modify', 'not-acceptable'
"Please fill in all fields.")
return
...
def _sendError(self, iq, code, error_type, name, text=''):
iq.reply().setPayload(iq['register'].xml)
iq.error()
iq['error']['code'] = code
iq['error']['type'] = error_type
iq['error']['condition'] = name
iq['error']['text'] = text
iq.send()
Cases 5 and 6: Conflicting and Successful Registration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We are down to the final decision on if we have a successful registration. We
send the user's data to the backend with the ``self.backend.register`` method.
If it returns ``True``, then registration has been successful. Otherwise,
there has been a conflict with usernames and registration has failed. Like
with unregistering an account, we trigger an event indicating that a user has
been registered by using ``self.xmpp.event('registered_user', iq)``. See the
component examples below for how to respond to this event.
.. code-block:: python
def __handleRegistration(self, iq):
if iq['type'] == 'get':
# Registration form requested
userData = self.backend[iq['from'].bare]
self.sendRegistrationForm(iq, userData)
elif iq['type'] == 'set':
if iq['register']['remove']:
# Remove an account
self.backend.unregister(iq['from'].bare)
self.xmpp.event('unregistered_user', iq)
iq.reply().send()
return
for field in self.form_fields:
if not iq['register'][field]:
# Incomplete Registration
self._sendError(iq, '406', 'modify', 'not-acceptable',
"Please fill in all fields.")
return
if self.backend.register(iq['from'].bare, iq['register']):
# Successful registration
self.xmpp.event('registered_user', iq)
iq.reply().setPayload(iq['register'].xml)
iq.send()
else:
# Conflicting registration
self._sendError(iq, '409', 'cancel', 'conflict',
"That username is already taken.")
Example Component Using the XEP-0077 Plugin
-------------------------------------------
Alright, the moment we've been working towards - actually using our plugin to
simplify our other applications. Here is a basic component that simply manages
user registrations and sends the user a welcoming message when they register,
and a farewell message when they delete their account.
Note that we have to register the ``'xep_0030'`` plugin first,
and that we specified the form fields we wish to use with
``self.xmpp.plugin['xep_0077'].setForm('username', 'password')``.
.. code-block:: python
import sleekxmpp.componentxmpp
class Example(sleekxmpp.componentxmpp.ComponentXMPP):
def __init__(self, jid, password):
sleekxmpp.componentxmpp.ComponentXMPP.__init__(self, jid, password, 'localhost', 8888)
self.registerPlugin('xep_0030')
self.registerPlugin('xep_0077')
self.plugin['xep_0077'].setForm('username', 'password')
self.add_event_handler("registered_user", self.reg)
self.add_event_handler("unregistered_user", self.unreg)
def reg(self, iq):
msg = "Welcome! %s" % iq['register']['username']
self.sendMessage(iq['from'], msg, mfrom=self.fulljid)
def unreg(self, iq):
msg = "Bye! %s" % iq['register']['username']
self.sendMessage(iq['from'], msg, mfrom=self.fulljid)
**Congratulations!** We now have a basic, functioning implementation of
XEP-0077.
Complete Source Code for XEP-0077 Plugin
----------------------------------------
Here is a copy of a more complete implementation of the plugin we created, but
with some additional registration fields implemented.
.. code-block:: python
"""
Creating a SleekXMPP Plugin
This is a minimal implementation of XEP-0077 to serve
as a tutorial for creating SleekXMPP plugins.
"""
from sleekxmpp.plugins.base import base_plugin
from sleekxmpp.xmlstream.handler.callback import Callback
from sleekxmpp.xmlstream.matcher.xpath import MatchXPath
from sleekxmpp.xmlstream import ElementBase, ET, JID, register_stanza_plugin
from sleekxmpp import Iq
import copy
class Registration(ElementBase):
namespace = 'jabber:iq:register'
name = 'query'
plugin_attrib = 'register'
interfaces = set(('username', 'password', 'email', 'nick', 'name',
'first', 'last', 'address', 'city', 'state', 'zip',
'phone', 'url', 'date', 'misc', 'text', 'key',
'registered', 'remove', 'instructions'))
sub_interfaces = interfaces
def getRegistered(self):
present = self.xml.find('{%s}registered' % self.namespace)
return present is not None
def getRemove(self):
present = self.xml.find('{%s}remove' % self.namespace)
return present is not None
def setRegistered(self, registered):
if registered:
self.addField('registered')
else:
del self['registered']
def setRemove(self, remove):
if remove:
self.addField('remove')
else:
del self['remove']
def addField(self, name):
itemXML = ET.Element('{%s}%s' % (self.namespace, name))
self.xml.append(itemXML)
class UserStore(object):
def __init__(self):
self.users = {}
def __getitem__(self, jid):
return self.users.get(jid, None)
def register(self, jid, registration):
username = registration['username']
def filter_usernames(user):
return user != jid and self.users[user]['username'] == username
conflicts = filter(filter_usernames, self.users.keys())
if conflicts:
return False
self.users[jid] = registration
return True
def unregister(self, jid):
del self.users[jid]
class xep_0077(base_plugin):
"""
XEP-0077 In-Band Registration
"""
def plugin_init(self):
self.description = "In-Band Registration"
self.xep = "0077"
self.form_fields = ('username', 'password')
self.form_instructions = ""
self.backend = UserStore()
self.xmpp.registerHandler(
Callback('In-Band Registration',
MatchXPath('{%s}iq/{jabber:iq:register}query' % self.xmpp.default_ns),
self.__handleRegistration))
register_stanza_plugin(Iq, Registration)
def post_init(self):
base_plugin.post_init(self)
self.xmpp['xep_0030'].add_feature("jabber:iq:register")
def __handleRegistration(self, iq):
if iq['type'] == 'get':
# Registration form requested
userData = self.backend[iq['from'].bare]
self.sendRegistrationForm(iq, userData)
elif iq['type'] == 'set':
if iq['register']['remove']:
# Remove an account
self.backend.unregister(iq['from'].bare)
self.xmpp.event('unregistered_user', iq)
iq.reply().send()
return
for field in self.form_fields:
if not iq['register'][field]:
# Incomplete Registration
self._sendError(iq, '406', 'modify', 'not-acceptable',
"Please fill in all fields.")
return
if self.backend.register(iq['from'].bare, iq['register']):
# Successful registration
self.xmpp.event('registered_user', iq)
iq.reply().setPayload(iq['register'].xml)
iq.send()
else:
# Conflicting registration
self._sendError(iq, '409', 'cancel', 'conflict',
"That username is already taken.")
def setForm(self, *fields):
self.form_fields = fields
def setInstructions(self, instructions):
self.form_instructions = instructions
def sendRegistrationForm(self, iq, userData=None):
reg = iq['register']
if userData is None:
userData = {}
else:
reg['registered'] = True
if self.form_instructions:
reg['instructions'] = self.form_instructions
for field in self.form_fields:
data = userData.get(field, '')
if data:
# Add field with existing data
reg[field] = data
else:
# Add a blank field
reg.addField(field)
iq.reply().setPayload(reg.xml)
iq.send()
def _sendError(self, iq, code, error_type, name, text=''):
iq.reply().setPayload(iq['register'].xml)
iq.error()
iq['error']['code'] = code
iq['error']['type'] = error_type
iq['error']['condition'] = name
iq['error']['text'] = text
iq.send()

271
docs/event_index.rst Normal file
View File

@@ -0,0 +1,271 @@
Event Index
===========
.. glossary::
:sorted:
connected
- **Data:** ``{}``
- **Source:** :py:class:`~sleekxmpp.clientxmpp.ClientXMPP`
Signal that a connection has been made with the XMPP server, but a session
has not yet been established.
changed_status
- **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~sleekxmpp.BaseXMPP`
Triggered when a presence stanza is received from a JID with a show type
different than the last presence stanza from the same JID.
changed_subscription
- **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~sleekxmpp.BaseXMPP`
Triggered whenever a presence stanza with a type of ``subscribe``,
``subscribed``, ``unsubscribe``, or ``unsubscribed`` is received.
Note that if the values ``xmpp.auto_authorize`` and ``xmpp.auto_subscribe``
are set to ``True`` or ``False``, and not ``None``, then SleekXMPP will
either accept or reject all subscription requests before your event handlers
are called. Set these values to ``None`` if you wish to make more complex
subscription decisions.
chatstate_active
- **Data:**
- **Source:**
chatstate_composing
- **Data:**
- **Source:**
chatstate_gone
- **Data:**
- **Source:**
chatstate_inactive
- **Data:**
- **Source:**
chatstate_paused
- **Data:**
- **Source:**
disco_info
- **Data:** :py:class:`~sleekxmpp.plugins.xep_0030.stanza.DiscoInfo`
- **Source:** :py:class:`~sleekxmpp.plugins.xep_0030.disco.xep_0030`
Triggered whenever a ``disco#info`` result stanza is received.
disco_items
- **Data:** :py:class:`~sleekxmpp.plugins.xep_0030.stanza.DiscoItems`
- **Source:** :py:class:`~sleekxmpp.plugins.xep_0030.disco.xep_0030`
Triggered whenever a ``disco#items`` result stanza is received.
disconnected
- **Data:** ``{}``
- **Source:** :py:class:`~sleekxmpp.ClientXMPP`
Signal that the connection with the XMPP server has been lost.
entity_time
- **Data:**
- **Source:**
failed_auth
- **Data:** ``{}``
- **Source:** :py:class:`~sleekxmpp.ClientXMPP`, :py:class:`~sleekxmpp.plugins.xep_0078.xep_0078`
Signal that the server has rejected the provided login credentials.
gmail_notify
- **Data:** ``{}``
- **Source:** :py:class:`~sleekxmpp.plugins.gmail_notify.gmail_notify`
Signal that there are unread emails for the Gmail account associated with the current XMPP account.
gmail_messages
- **Data:** :py:class:`~sleekxmpp.Iq`
- **Source:** :py:class:`~sleekxmpp.plugins.gmail_notify.gmail_notify`
Signal that there are unread emails for the Gmail account associated with the current XMPP account.
got_online
- **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~sleekxmpp.BaseXMPP`
If a presence stanza is received from a JID which was previously marked as
offline, and the presence has a show type of '``chat``', '``dnd``', '``away``',
or '``xa``', then this event is triggered as well.
got_offline
- **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~sleekxmpp.BaseXMPP`
Signal that an unavailable presence stanza has been received from a JID.
groupchat_invite
- **Data:**
- **Source:**
groupchat_direct_invite
- **Data:** :py:class:`~sleekxmpp.Message`
- **Source:** :py:class:`~sleekxmpp.plugins.xep_0249.direct`
groupchat_message
- **Data:** :py:class:`~sleekxmpp.Message`
- **Source:** :py:class:`~sleekxmpp.plugins.xep_0045.xep_0045`
Triggered whenever a message is received from a multi-user chat room.
groupchat_presence
- **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~sleekxmpp.plugins.xep_0045.xep_0045`
Triggered whenever a presence stanza is received from a user in a multi-user chat room.
groupchat_subject
- **Data:** :py:class:`~sleekxmpp.Message`
- **Source:** :py:class:`~sleekxmpp.plugins.xep_0045.xep_0045`
Triggered whenever the subject of a multi-user chat room is changed, or announced when joining a room.
killed
- **Data:**
- **Source:**
last_activity
- **Data:**
- **Source:**
message
- **Data:** :py:class:`~sleekxmpp.Message`
- **Source:** :py:class:`BaseXMPP <sleekxmpp.BaseXMPP>`
Makes the contents of message stanzas available whenever one is received. Be
sure to check the message type in order to handle error messages.
message_form
- **Data:** :py:class:`~sleekxmpp.plugins.xep_0004.Form`
- **Source:** :py:class:`~sleekxmpp.plugins.xep_0004.xep_0004`
Currently the same as :term:`message_xform`.
message_xform
- **Data:** :py:class:`~sleekxmpp.plugins.xep_0004.Form`
- **Source:** :py:class:`~sleekxmpp.plugins.xep_0004.xep_0004`
Triggered whenever a data form is received inside a message.
mucc::[room]::got_offline
- **Data:**
- **Source:**
muc::[room]::got_online
- **Data:**
- **Source:**
muc::[room]::message
- **Data:**
- **Source:**
muc::[room]::presence
- **Data:**
- **Source:**
presence_available
- **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``available``' is received.
presence_error
- **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``error``' is received.
presence_form
- **Data:** :py:class:`~sleekxmpp.plugins.xep_0004.Form`
- **Source:** :py:class:`~sleekxmpp.plugins.xep_0004.xep_0004`
This event is present in the XEP-0004 plugin code, but is currently not used.
presence_probe
- **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``probe``' is received.
presence_subscribe
- **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``subscribe``' is received.
presence_subscribed
- **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``subscribed``' is received.
presence_unavailable
- **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``unavailable``' is received.
presence_unsubscribe
- **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``unsubscribe``' is received.
presence_unsubscribed
- **Data:** :py:class:`~sleekxmpp.Presence`
- **Source:** :py:class:`~sleekxmpp.BaseXMPP`
A presence stanza with a type of '``unsubscribed``' is received.
roster_update
- **Data:** :py:class:`~sleekxmpp.stanza.Roster`
- **Source:** :py:class:`~sleekxmpp.ClientXMPP`
An IQ result containing roster entries is received.
sent_presence
- **Data:** ``{}``
- **Source:** :py:class:`BaseXMPP <sleekxmpp.BaseXMPP>`
Signal that an initial presence stanza has been written to the XML stream.
session_end
- **Data:** ``{}``
- **Source:** :py:class:`ClientXMPP <sleekxmpp.ClientXMPP>`,
:py:class:`ComponentXMPP <sleekxmpp.ComponentXMPP>`
:py:class:`XEP-0078 <sleekxmpp.plugins.xep_0078>`
Signal that a connection to the XMPP server has been lost and the current
stream session has ended. Currently equivalent to :term:`disconnected`, but
future implementation of `XEP-0198: Stream Management <http://xmpp.org/extensions/xep-0198.html>`_
will distinguish the two events.
Plugins that maintain session-based state should clear themselves when
this event is fired.
session_start
- **Data:** ``{}``
- **Source:** :py:class:`ClientXMPP <sleekxmpp.ClientXMPP>`,
:py:class:`ComponentXMPP <sleekxmpp.ComponentXMPP>`
:py:class:`XEP-0078 <sleekxmpp.plugins.xep_0078>`
Signal that a connection to the XMPP server has been made and a session has been established.
socket_error
- **Data:** ``Socket`` exception object
- **Source:** :py:class:`~sleekxmpp.xmlstream.XMLstream`
stream_error
- **Data:** :py:class:`~sleekxmpp.stanza.StreamError`
- **Source:** :py:class:`~sleekxmpp.BaseXMPP`

2
docs/features.rst Normal file
View File

@@ -0,0 +1,2 @@
How to Use Stream Features
==========================

View File

@@ -0,0 +1,75 @@
.. _echocomponent:
=================================
Create and Run a Server Component
=================================
.. note::
If you have any issues working through this quickstart guide
or the other tutorials here, please either send a message to the
`mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
or join the chat room at `sleek@conference.jabber.org
<xmpp:sleek@conference.jabber.org?join>`_.
If you have not yet installed SleekXMPP, do so now by either checking out a version
from `Github <http://github.com/fritzy/SleekXMPP>`_, or installing it using ``pip``
or ``easy_install``.
.. code-block:: sh
pip install sleekxmpp # Or: easy_install sleekxmpp
Many XMPP applications eventually graduate to requiring to run as a server
component in order to meet scalability requirements. To demonstrate how to
turn an XMPP client bot into a component, we'll turn the echobot example
(:ref:`echobot`) into a component version.
The first difference is that we will add an additional import statement:
.. code-block:: python
from sleekxmpp.componentxmpp import ComponentXMPP
Likewise, we will change the bot's class definition to match:
.. code-block:: python
class EchoComponent(ComponentXMPP):
def __init__(self, jid, secret, server, port):
ComponentXMPP.__init__(self, jid, secret, server, port)
A component instance requires two extra parameters compared to a client
instance: ``server`` and ``port``. These specifiy the name and port of
the XMPP server that will be accepting the component. For example, for
a MUC component, the following could be used:
.. code-block:: python
muc = ComponentXMPP('muc.sleekxmpp.com', '******', 'sleekxmpp.com', 5555)
.. note::
The ``server`` value is **NOT** derived from the provided JID for the
component, unlike with client connections.
One difference with the component version is that we do not have
to handle the :term:`session_start` event if we don't wish to deal
with presence.
The other, main difference with components is that the
``'from'`` value for every stanza must be explicitly set, since
components may send stanzas from multiple JIDs. To do so,
the :meth:`~sleekxmpp.basexmpp.BaseXMPP.send_message()` and
:meth:`~sleekxmpp.basexmpp.BaseXMPP.send_presence()` accept the parameters
``mfrom`` and ``pfrom``, respectively. For any method that uses
:class:`~sleekxmpp.stanza.iq.Iq` stanzas, ``ifrom`` may be used.
Final Product
-------------
.. include:: ../../examples/echo_component.py
:literal:

View File

@@ -0,0 +1,390 @@
.. _echobot:
===============================
SleekXMPP Quickstart - Echo Bot
===============================
.. note::
If you have any issues working through this quickstart guide
or the other tutorials here, please either send a message to the
`mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
or join the chat room at `sleek@conference.jabber.org
<xmpp:sleek@conference.jabber.org?join>`_.
If you have not yet installed SleekXMPP, do so now by either checking out a version
from `Github <http://github.com/fritzy/SleekXMPP>`_, or installing it using ``pip``
or ``easy_install``.
.. code-block:: sh
pip install sleekxmpp # Or: easy_install sleekxmpp
As a basic starting project, we will create an echo bot which will reply to any
messages sent to it. We will also go through adding some basic command line configuration
for enabling or disabling debug log outputs and setting the username and password
for the bot.
For the command line options processing, we will use the built-in ``optparse``
module and the ``getpass`` module for reading in passwords.
TL;DR Just Give Me the Code
---------------------------
As you wish: :ref:`the completed example <echobot_complete>`.
Overview
--------
To get started, here is a brief outline of the structure that the final project will have:
.. code-block:: python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import logging
import getpass
from optparse import OptionParser
import sleekxmpp
'''Here we will create out echo bot class'''
if __name__ == '__main__':
'''Here we will configure and read command line options'''
'''Here we will instantiate our echo bot'''
'''Finally, we connect the bot and start listening for messages'''
Default Encoding
----------------
XMPP requires support for UTF-8 and so SleekXMPP must use UTF-8 as well. In
Python3 this is simple because Unicode is the default string type. For Python2.6+
the situation is not as easy because standard strings are simply byte arrays and
use ASCII. We can get Python to use UTF-8 as the default encoding by including:
.. code-block:: python
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
.. warning::
Until we are able to ensure that SleekXMPP will always use Unicode in Python2.6+, this
may cause issues embedding SleekXMPP into other applications which assume ASCII encoding.
Creating the EchoBot Class
--------------------------
There are three main types of entities within XMPP — servers, components, and
clients. Since our echo bot will only be responding to a few people, and won't need
to remember thousands of users, we will use a client connection. A client connection
is the same type that you use with your standard IM client such as Pidgin or Psi.
SleekXMPP comes with a :class:`ClientXMPP <sleekxmpp.clientxmpp.ClientXMPP>` class
which we can extend to add our message echoing feature. :class:`ClientXMPP <sleekxmpp.clientxmpp.ClientXMPP>`
requires the parameters ``jid`` and ``password``, so we will let our ``EchoBot`` class accept those
as well.
.. code-block:: python
class EchoBot(sleekxmpp.ClientXMPP):
def __init__(self, jid, password):
super(EchoBot, self).__init__(jid, password)
Handling Session Start
~~~~~~~~~~~~~~~~~~~~~~
The XMPP spec requires clients to broadcast its presence and retrieve its roster (buddy list) once
it connects and establishes a session with the XMPP server. Until these two tasks are completed,
some servers may not deliver or send messages or presence notifications to the client. So we now
need to be sure that we retrieve our roster and send an initial presence once the session has
started. To do that, we will register an event handler for the :term:`session_start` event.
.. code-block:: python
def __init__(self, jid, password):
super(EchoBot, self).__init__(jid, password)
self.add_event_handler('session_start', self.start)
Since we want the method ``self.start`` to execute when the :term:`session_start` event is triggered,
we also need to define the ``self.start`` handler.
.. code-block:: python
def start(self, event):
self.send_presence()
self.get_roster()
.. warning::
Not sending an initial presence and retrieving the roster when using a client instance can
prevent your program from receiving presence notifications or messages depending on the
XMPP server you have chosen.
Our event handler, like every event handler, accepts a single parameter which typically is the stanza
that was received that caused the event. In this case, ``event`` will just be an empty dictionary since
there is no associated data.
Our first task of sending an initial presence is done using :meth:`send_presence <sleekxmpp.basexmpp.BaseXMPP.send_presence>`.
Calling :meth:`send_presence <sleekxmpp.basexmpp.BaseXMPP.send_presence>` without any arguments will send the simplest
stanza allowed in XMPP:
.. code-block:: xml
<presence />
The second requirement is fulfilled using :meth:`get_roster <sleekxmpp.clientxmpp.ClientXMPP.get_roster>`, which
will send an IQ stanza requesting the roster to the server and then wait for the response. You may be wondering
what :meth:`get_roster <sleekxmpp.clientxmpp.ClientXMPP.get_roster>` returns since we are not saving any return
value. The roster data is saved by an internal handler to ``self.roster``, and in the case of a :class:`ClientXMPP
<sleekxmpp.clientxmpp.ClientXMPP>` instance to ``self.client_roster``. (The difference between ``self.roster`` and
``self.client_roster`` is that ``self.roster`` supports storing roster information for multiple JIDs, which is useful
for components, whereas ``self.client_roster`` stores roster data for just the client's JID.)
It is possible for a timeout to occur while waiting for the server to respond, which can happen if the
network is excessively slow or the server is no longer responding. In that case, an :class:`IQTimeout
<sleekxmpp.exceptions.IQTimeout>` is raised. Similarly, an :class:`IQError <sleekxmpp.exceptions.IQError>` exception can
be raised if the request contained bad data or requested the roster for the wrong user. In either case, you can wrap the
``get_roster()`` call in a ``try``/``except`` block to retry the roster retrieval process.
The XMPP stanzas from the roster retrieval process could look like this:
.. code-block:: xml
<iq type="get">
<query xmlns="jabber:iq:roster" />
</iq>
<iq type="result" to="echobot@example.com" from="example.com">
<query xmlns="jabber:iq:roster">
<item jid="friend@example.com" subscription="both" />
</query>
</iq>
Responding to Messages
~~~~~~~~~~~~~~~~~~~~~~
Now that an ``EchoBot`` instance handles :term:`session_start`, we can begin receiving and
responding to messages. Now we can register a handler for the :term:`message` event that is raised
whenever a messsage is received.
.. code-block:: python
def __init__(self, jid, password):
super(EchoBot, self).__init__(jid, password)
self.add_event_handler('session_start', self.start)
self.add_event_handler('message', self.message)
The :term:`message` event is fired whenever a ``<message />`` stanza is received, including for
group chat messages, errors, etc. Properly responding to messages thus requires checking the
``'type'`` interface of the message :term:`stanza object`. For responding to only messages
addressed to our bot (and not from a chat room), we check that the type is either ``normal``
or ``chat``. (Other potential types are ``error``, ``headline``, and ``groupchat``.)
.. code-block:: python
def message(self, msg):
if msg['type'] in ('normal', 'chat'):
msg.reply("Thanks for sending:\n%s" % msg['body']).send()
Let's take a closer look at the ``.reply()`` method used above. For message stanzas,
``.reply()`` accepts the parameter ``body`` (also as the first positional argument),
which is then used as the value of the ``<body />`` element of the message.
Setting the appropriate ``to`` JID is also handled by ``.reply()``.
Another way to have sent the reply message would be to use :meth:`send_message <sleekxmpp.basexmpp.BaseXMPP.send_message>`,
which is a convenience method for generating and sending a message based on the values passed to it. If we were to use
this method, the above code would look as so:
.. code-block:: python
def message(self, msg):
if msg['type'] in ('normal', 'chat'):
self.send_message(mto=msg['from'],
mbody='Thanks for sending:\n%s' % msg['body'])
Whichever method you choose to use, the results in action will look like this:
.. code-block:: xml
<message to="echobot@example.com" from="someuser@example.net" type="chat">
<body>Hej!</body>
</message>
<message to="someuser@example.net" type="chat">
<body>Thanks for sending:
Hej!</body>
</message>
.. note::
XMPP does not require stanzas sent by a client to include a ``from`` attribute, and
leaves that responsibility to the XMPP server. However, if a sent stanza does
include a ``from`` attribute, it must match the full JID of the client or some
servers will reject it. SleekXMPP thus leaves out the ``from`` attribute when replying
using a client connection.
Command Line Arguments and Logging
----------------------------------
While this isn't part of SleekXMPP itself, we do want our echo bot program to be able
to accept a JID and password from the command line instead of hard coding them. We will
use the ``optparse`` module for this, though there are several alternative methods, including
the newer ``argparse`` module.
We want to accept three parameters: the JID for the echo bot, its password, and a flag for
displaying the debugging logs. We also want these to be optional parameters, since passing
a password directly through the command line can be a security risk.
.. code-block:: python
if __name__ == '__main__':
optp = OptionParser()
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
opts, args = optp.parse_args()
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
Since we included a flag for enabling debugging logs, we need to configure the
``logging`` module to behave accordingly.
.. code-block:: python
if __name__ == '__main__':
# .. option parsing from above ..
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
Connecting to the Server and Processing
---------------------------------------
There are three steps remaining until our echo bot is complete:
1. We need to instantiate the bot.
2. The bot needs to connect to an XMPP server.
3. We have to instruct the bot to start running and processing messages.
Creating the bot is straightforward, but we can also perform some configuration
at this stage. For example, let's say we want our bot to support `service discovery
<http://xmpp.org/extensions/xep-0030.html>`_ and `pings <http://xmpp.org/extensions/xep-0199.html>`_:
.. code-block:: python
if __name__ == '__main__':
# .. option parsing and logging steps from above
xmpp = EchoBot(opts.jid, opts.password)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0199') # Ping
If the ``EchoBot`` class had a hard dependency on a plugin, we could register that plugin in
the ``EchoBot.__init__`` method instead.
.. note::
If you are using the OpenFire server, you will need to include an additional
configuration step. OpenFire supports a different version of SSL than what
most servers and SleekXMPP support.
.. code-block:: python
import ssl
xmpp.ssl_version = ssl.PROTOCOL_SSLv3
Now we're ready to connect and begin echoing messages. If you have the package
``dnspython`` installed, then the :meth:`sleekxmpp.clientxmpp.ClientXMPP` method
will perform a DNS query to find the appropriate server to connect to for the
given JID. If you do not have ``dnspython``, then SleekXMPP will attempt to
connect to the hostname used by the JID, unless an address tuple is supplied
to :meth:`sleekxmpp.clientxmpp.ClientXMPP`.
.. code-block:: python
if __name__ == '__main__':
# .. option parsing & echo bot configuration
if xmpp.connect():
xmpp.process(block=True)
else:
print('Unable to connect')
.. note::
For Google Talk users withouth ``dnspython`` installed, the above code
should look like:
.. code-block:: python
if __name__ == '__main__':
# .. option parsing & echo bot configuration
if xmpp.connect(('talk.google.com', 5222)):
xmpp.process(block=True)
else:
print('Unable to connect')
To begin responding to messages, you'll see we called :meth:`sleekxmpp.basexmpp.BaseXMPP.process`
which will start the event handling, send queue, and XML reader threads. It will also call
the :meth:`sleekxmpp.plugins.base.base_plugin.post_init` method on all registered plugins. By
passing ``block=True`` to :meth:`sleekxmpp.basexmpp.BaseXMPP.process` we are running the
main processing loop in the main thread of execution. The :meth:`sleekxmpp.basexmpp.BaseXMPP.process`
call will not return until after SleekXMPP disconnects. If you need to run the client in the background
for another program, use ``block=False`` to spawn the processing loop in its own thread.
.. note::
Before 1.0, controlling the blocking behaviour of :meth:`sleekxmpp.basexmpp.BaseXMPP.process` was
done via the ``threaded`` argument. This arrangement was a source of confusion because some users
interpreted that as controlling whether or not SleekXMPP used threads at all, instead of how
the processing loop itself was spawned.
The statements ``xmpp.process(threaded=False)`` and ``xmpp.process(block=True)`` are equivalent.
.. _echobot_complete:
The Final Product
-----------------
Here then is what the final result should look like after working through the guide above. The code
can also be found in the SleekXMPP `examples directory <http://github.com/fritzy/SleekXMPP/tree/master/examples>`_.
.. compound::
You can run the code using:
.. code-block:: sh
python echobot.py -d -j echobot@example.com
which will prompt for the password and then begin echoing messages. To test, open
your regular IM client and start a chat with the echo bot. Messages you send to it should
be mirrored back to you. Be careful if you are using the same JID for the echo bot that
you also have logged in with another IM client. Messages could be routed to your IM client instead
of the bot.
.. include:: ../../examples/echo_client.py
:literal:

182
docs/getting_started/iq.rst Normal file
View File

@@ -0,0 +1,182 @@
Send/Receive IQ Stanzas
=======================
Unlike :class:`~sleekxmpp.stanza.message.Message` and
:class:`~sleekxmpp.stanza.presence.Presence` stanzas which only use
text data for basic usage, :class:`~sleekxmpp.stanza.iq.Iq` stanzas
require using XML payloads, and generally entail creating a new
SleekXMPP plugin to provide the necessary convenience methods to
make working with them easier.
Basic Use
---------
XMPP's use of :class:`~sleekxmpp.stanza.iq.Iq` stanzas is built around
namespaced ``<query />`` elements. For clients, just sending the
empty ``<query />`` element will suffice for retrieving information. For
example, a very basic implementation of service discovery would just
need to be able to send:
.. code-block:: xml
<iq to="user@example.com" type="get" id="1">
<query xmlns="http://jabber.org/protocol/disco#info" />
</iq>
Creating Iq Stanzas
~~~~~~~~~~~~~~~~~~~
SleekXMPP provides built-in support for creating basic :class:`~sleekxmpp.stanza.iq.Iq`
stanzas this way. The relevant methods are:
* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq`
* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_get`
* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_set`
* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_result`
* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_error`
* :meth:`~sleekxmpp.basexmpp.BaseXMPP.make_iq_query`
These methods all follow the same pattern: create or modify an existing
:class:`~sleekxmpp.stanza.iq.Iq` stanza, set the ``'type'`` value based
on the method name, and finally add a ``<query />`` element with the given
namespace. For example, to produce the query above, you would use:
.. code-block:: python
self.make_iq_get(queryxmlns='http://jabber.org/protocol/disco#info',
ito='user@example.com')
Sending Iq Stanzas
~~~~~~~~~~~~~~~~~~
Once an :class:`~sleekxmpp.stanza.iq.Iq` stanza is created, sending it
over the wire is done using its :meth:`~sleekxmpp.stanza.iq.Iq.send()`
method, like any other stanza object. However, there are a few extra
options to control how to wait for the query's response.
These options are:
* ``block``: The default behaviour is that :meth:`~sleekxmpp.stanza.iq.Iq.send()`
will block until a response is received and the response stanza will be the
return value. Setting ``block`` to ``False`` will cause the call to return
immediately. In which case, you will need to arrange some way to capture
the response stanza if you need it.
* ``timeout``: When using the blocking behaviour, the call will eventually
timeout with an error. The default timeout is 30 seconds, but this may
be overidden two ways. To change the timeout globally, set:
.. code-block:: python
self.response_timeout = 10
To change the timeout for a single call, the ``timeout`` parameter works:
.. code-block:: python
iq.send(timeout=60)
* ``callback``: When not using a blocking call, using the ``callback``
argument is a simple way to register a handler that will execute
whenever a response is finally received. Using this method, there
is no timeout limit. In case you need to remove the callback, the
name of the newly created callback is returned.
.. code-block:: python
cb_name = iq.send(callback=self.a_callback)
# ... later if we need to cancel
self.remove_handler(cb_name)
Properly working with :class:`~sleekxmpp.stanza.iq.Iq` stanzas requires
handling the intended, normal flow, error responses, and timed out
requests. To make this easier, two exceptions may be thrown by
:meth:`~sleekxmpp.stanza.iq.Iq.send()`: :exc:`~sleekxmpp.exceptions.IqError`
and :exc:`~sleekxmpp.exceptions.IqTimeout`. These exceptions only
apply to the default, blocking calls.
.. code-block:: python
try:
resp = iq.send()
# ... do stuff with expected Iq result
except IqError as e:
err_resp = e.iq
# ... handle error case
except IqTimeout:
# ... no response received in time
pass
If you do not care to distinguish between errors and timeouts, then you
can combine both cases with a generic :exc:`~sleekxmpp.exceptions.XMPPError`
exception:
.. code-block:: python
try:
resp = iq.send()
except XMPPError:
# ... Don't care about the response
pass
Advanced Use
------------
Going beyond the basics provided by SleekXMPP requires building at least a
rudimentary SleekXMPP plugin to create a :term:`stanza object` for
interfacting with the :class:`~sleekxmpp.stanza.iq.Iq` payload.
.. seealso::
* :ref:`create-plugin`
* :ref:`work-with-stanzas`
* :ref:`using-handlers-matchers`
The typical way to respond to :class:`~sleekxmpp.stanza.iq.Iq` requests is
to register stream handlers. As an example, suppose we create a stanza class
named ``CustomXEP`` which uses the XML element ``<query xmlns="custom-xep" />``,
and has a :attr:`~sleekxmpp.xmlstream.stanzabase.ElementBase.plugin_attrib` value
of ``custom_xep``.
There are two types of incoming :class:`~sleekxmpp.stanza.iq.Iq` requests:
``get`` and ``set``. You can register a handler that will accept both and then
filter by type as needed, as so:
.. code-block:: python
self.register_handler(Callback(
'CustomXEP Handler',
StanzaPath('iq/custom_xep'),
self._handle_custom_iq))
# ...
def _handle_custom_iq(self, iq):
if iq['type'] == 'get':
# ...
pass
elif iq['type'] == 'set':
# ...
pass
else:
# ... This will capture error responses too
pass
If you want to filter out query types beforehand, you can adjust the matching
filter by using ``@type=get`` or ``@type=set`` if you are using the recommended
:class:`~sleekxmpp.xmlstream.matcher.stanzapath.StanzaPath` matcher.
.. code-block:: python
self.register_handler(Callback(
'CustomXEP Handler',
StanzaPath('iq@type=get/custom_xep'),
self._handle_custom_iq_get))
# ...
def _handle_custom_iq_get(self, iq):
assert(iq['type'] == 'get')

View File

@@ -0,0 +1,208 @@
.. _mucbot:
=========================
Mulit-User Chat (MUC) Bot
=========================
.. note::
If you have any issues working through this quickstart guide
or the other tutorials here, please either send a message to the
`mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
or join the chat room at `sleek@conference.jabber.org
<xmpp:sleek@conference.jabber.org?join>`_.
If you have not yet installed SleekXMPP, do so now by either checking out a version
from `Github <http://github.com/fritzy/SleekXMPP>`_, or installing it using ``pip``
or ``easy_install``.
.. code-block:: sh
pip install sleekxmpp # Or: easy_install sleekxmpp
Now that you've got the basic gist of using SleekXMPP by following the
echobot example (:ref:`echobot`), we can use one of the bundled plugins
to create a very popular XMPP starter project: a `Multi-User Chat`_
(MUC) bot. Our bot will login to an XMPP server, join an MUC chat room
and "lurk" indefinitely, responding with a generic message to anyone
that mentions its nickname. It will also greet members as they join the
chat room.
.. _`multi-user chat`: http://xmpp.org/extensions/xep-0045.html
Joining The Room
----------------
As usual, our code will be based on the pattern explained in :ref:`echobot`.
To start, we create an ``MUCBot`` class based on
:class:`ClientXMPP <sleekxmpp.clientxmpp.ClientXMPP>` and which accepts
parameters for the JID of the MUC room to join, and the nick that the
bot will use inside the chat room. We also register an
:term:`event handler` for the :term:`session_start` event.
.. code-block:: python
import sleekxmpp
class MUCBot(sleekxmpp.ClientXMPP):
def __init__(self, jid, password, room, nick):
sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.room = room
self.nick = nick
self.add_event_handler("session_start", self.start)
After initialization, we also need to register the MUC (XEP-0045) plugin
so that we can make use of the group chat plugin's methods and events.
.. code-block:: python
xmpp.register_plugin('xep_0045')
Finally, we can make our bot join the chat room once an XMPP session
has been established:
.. code-block:: python
def start(self, event):
self.get_roster()
self.send_presence()
self.plugin['xep_0045'].joinMUC(self.room,
self.nick,
wait=True)
Note that as in :ref:`echobot`, we need to include send an initial presence and request
the roster. Next, we want to join the group chat, so we call the
``joinMUC`` method of the MUC plugin.
.. note::
The :attr:`plugin <sleekxmpp.basexmpp.BaseXMPP.plugin>` attribute is
dictionary that maps to instances of plugins that we have previously
registered, by their names.
Adding Functionality
--------------------
Currently, our bot just sits dormantly inside the chat room, but we
would like it to respond to two distinct events by issuing a generic
message in each case to the chat room. In particular, when a member
mentions the bot's nickname inside the chat room, and when a member
joins the chat room.
Responding to Mentions
~~~~~~~~~~~~~~~~~~~~~~
Whenever a user mentions our bot's nickname in chat, our bot will
respond with a generic message resembling *"I heard that, user."* We do
this by examining all of the messages sent inside the chat and looking
for the ones which contain the nickname string.
First, we register an event handler for the :term:`groupchat_message`
event inside the bot's ``__init__`` function.
.. note::
We do not register a handler for the :term:`message` event in this
bot, but if we did, the group chat message would have been sent to
both handlers.
.. code-block:: python
def __init__(self, jid, password, room, nick):
sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.room = room
self.nick = nick
self.add_event_handler("session_start", self.start)
self.add_event_handler("groupchat_message", self.muc_message)
Then, we can send our generic message whenever the bot's nickname gets
mentioned.
.. warning::
Always check that a message is not from yourself,
otherwise you will create an infinite loop responding
to your own messages.
.. code-block:: python
def muc_message(self, msg):
if msg['mucnick'] != self.nick and self.nick in msg['body']:
self.send_message(mto=msg['from'].bare,
mbody="I heard that, %s." % msg['mucnick'],
mtype='groupchat')
Greeting Members
~~~~~~~~~~~~~~~~
Now we want to greet member whenever they join the group chat. To
do this we will use the dynamic ``muc::room@server::got_online`` [1]_
event so it's a good idea to register an event handler for it.
.. note::
The groupchat_presence event is triggered whenever a
presence stanza is received from any chat room, including
any presences you send yourself. To limit event handling
to a single room, use the events ``muc::room@server::presence``,
``muc::room@server::got_online``, or ``muc::room@server::got_offline``.
.. code-block:: python
def __init__(self, jid, password, room, nick):
sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.room = room
self.nick = nick
self.add_event_handler("session_start", self.start)
self.add_event_handler("groupchat_message", self.muc_message)
self.add_event_handler("muc::%s::got_online" % self.room,
self.muc_online)
Now all that's left to do is to greet them:
.. code-block:: python
def muc_online(self, presence):
if presence['muc']['nick'] != self.nick:
self.send_message(mto=presence['from'].bare,
mbody="Hello, %s %s" % (presence['muc']['role'],
presence['muc']['nick']),
mtype='groupchat')
.. [1] this is similar to the :term:`got_online` event and is sent by
the xep_0045 plugin whenever a member joins the referenced
MUC chat room.
Final Product
-------------
.. compound::
The final step is to create a small runner script for initialising our ``MUCBot`` class and adding some
basic configuration options. By following the basic boilerplate pattern in :ref:`echobot`, we arrive
at the code below. To experiment with this example, you can use:
.. code-block:: sh
python muc.py -d -j jid@example.com -r room@muc.example.net -n lurkbot
which will prompt for the password, log in, and join the group chat. To test, open
your regular IM client and join the same group chat that you sent the bot to. You
will see ``lurkbot`` as one of the members in the group chat, and that it greeted
you upon entry. Send a message with the string "lurkbot" inside the body text, and you
will also see that it responds with our pre-programmed customized message.
.. include:: ../../examples/muc.py
:literal:

View File

@@ -0,0 +1,2 @@
Manage Presence Subscriptions
=============================

View File

@@ -0,0 +1,42 @@
.. _proxy:
=========================
Enable HTTP Proxy Support
=========================
.. note::
If you have any issues working through this quickstart guide
or the other tutorials here, please either send a message to the
`mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
or join the chat room at `sleek@conference.jabber.org
<xmpp:sleek@conference.jabber.org?join>`_.
In some instances, you may wish to route XMPP traffic through
an HTTP proxy, probably to get around restrictive firewalls.
SleekXMPP provides support for basic HTTP proxying with DIGEST
authentication.
Enabling proxy support is done in two steps. The first is to instruct SleekXMPP
to use a proxy, and the second is to configure the proxy details:
.. code-block:: python
xmpp = ClientXMPP(...)
xmpp.use_proxy = True
xmpp.proxy_config = {
'host': 'proxy.example.com',
'port': 5555,
'username': 'example_user',
'password': '******'
}
The ``'username'`` and ``'password'`` fields are optional if the proxy does not
require authentication.
The Final Product
-----------------
.. include:: ../../examples/proxy_echo_client.py
:literal:

View File

@@ -0,0 +1,2 @@
Send a Message Every 5 Minutes
==============================

View File

@@ -0,0 +1,94 @@
Sign in, Send a Message, and Disconnect
=======================================
.. note::
If you have any issues working through this quickstart guide
or the other tutorials here, please either send a message to the
`mailing list <http://groups.google.com/group/sleekxmpp-discussion>`_
or join the chat room at `sleek@conference.jabber.org
<xmpp:sleek@conference.jabber.org?join>`_.
A common use case for SleekXMPP is to send one-off messages from
time to time. For example, one use case could be sending out a notice when
a shell script finishes a task.
We will create our one-shot bot based on the pattern explained in :ref:`echobot`. To
start, we create a client class based on :class:`ClientXMPP <sleekxmpp.clientxmpp.ClientXMPP>` and
register a handler for the :term:`session_start` event. We will also accept parameters
for the JID that will receive our message, and the string content of the message.
.. code-block:: python
import sleekxmpp
class SendMsgBot(sleekxmpp.ClientXMPP):
def __init__(self, jid, password, recipient, msg):
super(SendMsgBot, self).__init__(jid, password)
self.recipient = recipient
self.msg = msg
self.add_event_handler('session_start', self.start)
def start(self, event):
self.send_presence()
self.get_roster()
Note that as in :ref:`echobot`, we need to include send an initial presence and request
the roster. Next, we want to send our message, and to do that we will use :meth:`send_message <sleekxmpp.basexmpp.BaseXMPP.send_message>`.
.. code-block:: python
def start(self, event):
self.send_presence()
self.get_roster()
self.send_message(mto=self.recipient, mbody=self.msg)
Finally, we need to disconnect the client using :meth:`disconnect <sleekxmpp.xmlstream.XMLStream.disconnect>`.
Now, sent stanzas are placed in a queue to pass them to the send thread. If we were to call
:meth:`disconnect <sleekxmpp.xmlstream.XMLStream.disconnect>` without any parameters, then it is possible
for the client to disconnect before the send queue is processed and the message is actually
sent on the wire. To ensure that our message is processed, we use
:meth:`disconnect(wait=True) <sleekxmpp.xmlstream.XMLStream.disconnect>`.
.. code-block:: python
def start(self, event):
self.send_presence()
self.get_roster()
self.send_message(mto=self.recipient, mbody=self.msg)
self.disconnect(wait=True)
.. warning::
If you happen to be adding stanzas to the send queue faster than the send thread
can process them, then :meth:`disconnect(wait=True) <sleekxmpp.xmlstream.XMLStream.disconnect>`
will block and not disconnect.
Final Product
-------------
.. compound::
The final step is to create a small runner script for initialising our ``SendMsgBot`` class and adding some
basic configuration options. By following the basic boilerplate pattern in :ref:`echobot`, we arrive
at the code below. To experiment with this example, you can use:
.. code-block:: sh
python send_client.py -d -j oneshot@example.com -t someone@example.net -m "This is a message"
which will prompt for the password and then log in, send your message, and then disconnect. To test, open
your regular IM client with the account you wish to send messages to. When you run the ``send_client.py``
example and instruct it to send your IM client account a message, you should receive the message you
gave. If the two JIDs you use also have a mutual presence subscription (they're on each other's buddy lists)
then you will also see the ``SendMsgBot`` client come online and then go offline.
.. include:: ../../examples/send_client.py
:literal:

35
docs/glossary.rst Normal file
View File

@@ -0,0 +1,35 @@
.. _glossary:
Glossary
========
.. glossary::
:sorted:
stream handler
A callback function that accepts stanza objects pulled directly
from the XML stream. A stream handler is encapsulated in a
object that includes a :term:`Matcher` object, and which provides
additional semantics. For example, the ``Waiter`` handler wrapper
blocks thread execution until a matching stanza is received.
event handler
A callback function that responds to events raised by
``XMLStream.event``. An event handler may be marked as
threaded, allowing it to execute outside of the main processing
loop.
stanza object
Informally may refer both to classes which extend ``ElementBase``
or ``StanzaBase``, and to objects of such classes.
A stanza object is a wrapper for an XML object which exposes ``dict``
like interfaces which may be assigned to, read from, or deleted.
stanza plugin
A :term:`stanza object` which has been registered as a potential child
of another stanza object. The plugin stanza may accessed through the
parent stanza using the plugin's ``plugin_attrib`` as an interface.
substanza
See :term:`stanza plugin`

201
docs/guide_xep_0030.rst Normal file
View File

@@ -0,0 +1,201 @@
XEP-0030: Working with Service Discovery
========================================
XMPP networks can be composed of many individual clients, components,
and servers. Determining the JIDs for these entities and the various
features they may support is the role of `XEP-0030, Service
Discovery <http://xmpp.org/extensions/xep-0030.html>`_, or "disco" for short.
Every XMPP entity may possess what are called nodes. A node is just a name for
some aspect of an XMPP entity. For example, if an XMPP entity provides `Ad-Hoc
Commands <http://xmpp.org/extensions/xep-0050.html>`_, then it will have a node
named ``http://jabber.org/protocol/commands`` which will contain information
about the commands provided. Other agents using these ad-hoc commands will
interact with the information provided by this node. Note that the node name is
just an identifier; there is no inherent meaning.
Working with service discovery is about creating and querying these nodes.
According to XEP-0030, a node may contain three types of information:
identities, features, and items. (Further, extensible, information types are
defined in `XEP-0128 <http://xmpp.org/extensions/xep-0128.html>`_, but they are
not yet implemented by SleekXMPP.) SleekXMPP provides methods to configure each
of these node attributes.
Configuring Service Discovery
-----------------------------
The design focus for the XEP-0030 plug-in is handling info and items requests
in a dynamic fashion, allowing for complex policy decisions of who may receive
information and how much, or use alternate backend storage mechanisms for all
of the disco data. To do this, each action that the XEP-0030 plug-in performs
is handed off to what is called a "node handler," which is just a callback
function. These handlers are arranged in a hierarchy that allows for a single
handler to manage an entire domain of JIDs (say for a component), while allowing
other handler functions to override that global behaviour for certain JIDs, or
even further limited to only certain JID and node combinations.
The Dynamic Handler Hierarchy
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ``global``: (JID is None, node is None)
Handlers assigned at this level for an action (such as ``add_feature``) provide a global default
behaviour when the action is performed.
* ``jid``: (JID assigned, node is None)
At this level, handlers provide a default behaviour for actions affecting any node owned by the
JID in question. This level is most useful for component connections; there is effectively no
difference between this and the global level when using a client connection.
* ``node``: (JID assigned, node assigned)
A handler for this level is responsible for carrying out an action for only one node, and is the
most specific handler type available. These types of handlers will be most useful for "special"
nodes that require special processing different than others provided by the JID, such as using
access control lists, or consolidating data from other nodes.
Default Static Handlers
~~~~~~~~~~~~~~~~~~~~~~~
The XEP-0030 plug-in provides a default set of handlers that work using in-memory
disco stanzas. Each handler simply performs the appropriate lookup or storage
operation using these stanzas without doing any complex operations such as
checking an ACL, etc.
You may find it necessary at some point to revert a particular node or JID to
using the default, static handlers. To do so, use the method ``make_static()``.
You may also elect to only convert a given set of actions instead.
Creating a Node Handler
~~~~~~~~~~~~~~~~~~~~~~~
Every node handler receives three arguments: the JID, the node, and a data
parameter that will contain the relevant information for carrying out the
handler's action, typically a dictionary.
The JID will always have a value, defaulting to ``xmpp.boundjid.full`` for
components or ``xmpp.boundjid.bare`` for clients. The node value may be None or
a string.
Only handlers for the actions ``get_info`` and ``get_items`` need to have return
values. For these actions, DiscoInfo or DiscoItems stanzas are exepected as
output. It is also acceptable for handlers for these actions to generate an
XMPPError exception when necessary.
Example Node Handler:
+++++++++++++++++++++
Here is one of the built-in default handlers as an example:
.. code-block:: python
def add_identity(self, jid, node, data):
"""
Add a new identity to the JID/node combination.
The data parameter may provide:
category -- The general category to which the agent belongs.
itype -- A more specific designation with the category.
name -- Optional human readable name for this identity.
lang -- Optional standard xml:lang value.
"""
self.add_node(jid, node)
self.nodes[(jid, node)]['info'].add_identity(
data.get('category', ''),
data.get('itype', ''),
data.get('name', None),
data.get('lang', None))
Adding Identities, Features, and Items
--------------------------------------
In order to maintain some backwards compatibility, the methods ``add_identity``,
``add_feature``, and ``add_item`` do not follow the method signature pattern of
the other API methods (i.e. jid, node, then other options), but rather retain
the parameter orders from previous plug-in versions.
Adding an Identity
~~~~~~~~~~~~~~~~~~
Adding an identity may be done using either the older positional notation, or
with keyword parameters. The example below uses the keyword arguments, but in
the same order as expected using positional arguments.
.. code-block:: python
xmpp['xep_0030'].add_identity(category='client',
itype='bot',
name='Sleek',
node='foo',
jid=xmpp.boundjid.full,
lang='no')
The JID and node values determine which handler will be used to perform the
``add_identity`` action.
The ``lang`` parameter allows for adding localized versions of identities using
the ``xml:lang`` attribute.
Adding a Feature
~~~~~~~~~~~~~~~~
The position ordering for ``add_feature()`` is to include the feature, then
specify the node and then the JID. The JID and node values determine which
handler will be used to perform the ``add_feature`` action.
.. code-block:: python
xmpp['xep_0030'].add_feature(feature='jabber:x:data',
node='foo',
jid=xmpp.boundjid.full)
Adding an Item
~~~~~~~~~~~~~~
The parameters to ``add_item()`` are potentially confusing due to the fact that
adding an item requires two JID and node combinations: the JID and node of the
item itself, and the JID and node that will own the item.
.. code-block:: python
xmpp['xep_0030'].add_item(jid='myitemjid@example.com',
name='An Item!',
node='owner_node',
subnode='item_node',
ijid=xmpp.boundjid.full)
.. note::
In this case, the owning JID and node are provided with the
parameters ``ijid`` and ``node``.
Peforming Disco Queries
-----------------------
The methods ``get_info()`` and ``get_items()`` are used to query remote JIDs
and their nodes for disco information. Since these methods are wrappers for
sending Iq stanzas, they also accept all of the parameters of the ``Iq.send()``
method. The ``get_items()`` method may also accept the boolean parameter
``iterator``, which when set to ``True`` will return an iterator object using
the `XEP-0059 <http://xmpp.org/extensions/xep-0059.html>`_ plug-in.
.. code-block:: python
info = self['xep_0030'].get_info(jid='foo@example.com',
node='bar',
ifrom='baz@mycomponent.example.com',
block=True,
timeout=30)
items = self['xep_0030'].get_info(jid='foo@example.com',
node='bar',
iterator=True)
For more examples on how to use basic disco queries, check the ``disco_browser.py``
example in the ``examples`` directory.
Local Queries
~~~~~~~~~~~~~
In some cases, it may be necessary to query the contents of a node owned by the
client itself, or one of a component's many JIDs. The same method is used as for
normal queries, with two differences. First, the parameter ``local=True`` must
be used. Second, the return value will be a DiscoInfo or DiscoItems stanza, not
a full Iq stanza.
.. code-block:: python
info = self['xep_0030'].get_info(node='foo', local=True)
items = self['xep_0030'].get_items(jid='somejid@mycomponent.example.com',
node='bar',
local=True)

View File

@@ -0,0 +1,4 @@
.. _using-handlers-matchers:
Using Stream Handlers and Matchers
==================================

30
docs/howto/stanzas.rst Normal file
View File

@@ -0,0 +1,30 @@
.. _work-with-stanzas:
How to Work with Stanza Objects
===============================
.. _create-stanza-interfaces:
Defining Stanza Interfaces
--------------------------
.. _create-stanza-plugins:
Creating Stanza Plugins
-----------------------
.. _create-extension-plugins:
Creating a Stanza Extension
---------------------------
.. _override-parent-interfaces:
Overriding a Parent Stanza
--------------------------

252
docs/index.rst Normal file
View File

@@ -0,0 +1,252 @@
SleekXMPP
#########
.. sidebar:: Get the Code
.. code-block:: sh
pip install sleekxmpp
The latest source code for SleekXMPP may be found on `Github
<http://github.com/fritzy/SleekXMPP>`_. Releases can be found in the
``master`` branch, while the latest development version is in the
``develop`` branch.
**Latest Stable Release**
- `1.0 <http://github.com/fritzy/SleekXMPP/zipball/1.0>`_
**Develop Releases**
- `Latest Develop Version <http://github.com/fritzy/SleekXMPP/zipball/develop>`_
A mailing list and XMPP chat room are available for discussing and getting
help with SleekXMPP.
**Mailing List**
`SleekXMPP Discussion on Google Groups <http://groups.google.com/group/sleekxmpp-discussion>`_
**Chat**
`sleek@conference.jabber.org <xmpp:sleek@conference.jabber.org?join>`_
SleekXMPP is an :ref:`MIT licensed <license>` XMPP library for Python 2.6/3.1+,
and is featured in examples in
`XMPP: The Definitive Guide <http://oreilly.com/catalog/9780596521271>`_
by Kevin Smith, Remko Tronçon, and Peter Saint-Andre. If you've arrived
here from reading the Definitive Guide, please see the notes on updating
the examples to the latest version of SleekXMPP.
SleekXMPP's design goals and philosphy are:
**Low number of dependencies**
Installing and using SleekXMPP should be as simple as possible, without
having to deal with long dependency chains.
As part of reducing the number of dependencies, some third party
modules are included with SleekXMPP in the ``thirdparty`` directory.
Imports from this module first try to import an existing installed
version before loading the packaged version, when possible.
**Every XEP as a plugin**
Following Python's "batteries included" approach, the goal is to
provide support for all currently active XEPs (final and draft). Since
adding XEP support is done through easy to create plugins, the hope is
to also provide a solid base for implementing and creating experimental
XEPs.
**Rewarding to work with**
As much as possible, SleekXMPP should allow things to "just work" using
sensible defaults and appropriate abstractions. XML can be ugly to work
with, but it doesn't have to be that way.
Here's your first SleekXMPP Bot:
--------------------------------
.. code-block:: python
import logging
from sleekxmpp import ClientXMPP
from sleekxmpp.exceptions import IqError, IqTimeout
class EchoBot(ClientXMPP):
def __init__(self, jid, password):
ClientXMPP.__init__(self, jid, password)
self.add_event_handler("session_start", self.session_start)
self.add_event_handler("message", self.message)
# If you wanted more functionality, here's how to register plugins:
# self.register_plugin('xep_0030') # Service Discovery
# self.register_plugin('xep_0199') # XMPP Ping
# Here's how to access plugins once you've registered them:
# self['xep_0030'].add_feature('echo_demo')
# If you are working with an OpenFire server, you will
# need to use a different SSL version:
# import ssl
# self.ssl_version = ssl.PROTOCOL_SSLv3
def session_start(self, event):
self.send_presence()
self.get_roster()
# Most get_*/set_* methods from plugins use Iq stanzas, which
# can generate IqError and IqTimeout exceptions
#
# try:
# self.get_roster()
# except IqError as err:
# logging.error('There was an error getting the roster')
# logging.error(err.iq['error']['condition'])
# self.disconnect()
# except IqTimeout:
# logging.error('Server is taking too long to respond')
# self.disconnect()
def message(self, msg):
if msg['type'] in ('chat', 'normal'):
msg.reply("Thanks for sending\n%(body)s" % msg).send()
if __name__ == '__main__':
# Ideally use optparse or argparse to get JID,
# password, and log level.
logging.basicConfig(level=logging.DEBUG,
format='%(levelname)-8s %(message)s')
xmpp = EchoBot('somejid@example.com', 'use_getpass')
xmpp.connect()
xmpp.process(block=True)
Getting Started (with Examples)
-------------------------------
.. toctree::
:maxdepth: 1
getting_started/echobot
getting_started/sendlogout
getting_started/component
getting_started/presence
getting_started/muc
getting_started/proxy
getting_started/scheduler
getting_started/iq
Tutorials, FAQs, and How To Guides
----------------------------------
.. toctree::
:maxdepth: 1
faq
xeps
xmpp_tdg
howto/stanzas
create_plugin
features
sasl
handlersmatchers
Plugin Guides
~~~~~~~~~~~~~
.. toctree::
:maxdepth: 1
guide_xep_0030
SleekXMPP Architecture and Design
---------------------------------
.. toctree::
:maxdepth: 3
architecture
plugin_arch
API Reference
-------------
.. toctree::
:maxdepth: 2
event_index
api/clientxmpp
api/componentxmpp
api/basexmpp
api/exceptions
api/xmlstream/jid
api/xmlstream/stanzabase
api/xmlstream/handler
api/xmlstream/matcher
api/xmlstream/xmlstream
api/xmlstream/scheduler
api/xmlstream/tostring
api/xmlstream/filesocket
Core Stanzas
~~~~~~~~~~~~
.. toctree::
:maxdepth: 2
api/stanza/rootstanza
api/stanza/message
api/stanza/presence
api/stanza/iq
api/stanza/error
api/stanza/stream_error
Plugins
~~~~~~~
.. toctree::
:maxdepth: 2
Additional Info
---------------
.. toctree::
:hidden:
glossary
license
* :ref:`license`
* :ref:`glossary`
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
Credits
-------
**Main Author:** `Nathan Fritz <http://andyet.net/team/fritzy>`_
`fritzy@netflint.net <xmpp:fritzy@netflint.net?message>`_,
`@fritzy <http://twitter.com/fritzy>`_
Nathan is also the author of XMPPHP and `Seesmic-AS3-XMPP
<http://code.google.com/p/seesmic-as3-xmpp/>`_, and a former member of the XMPP
Council.
**Co-Author:** `Lance Stout <http://andyet.net/team/lance>`_
`lancestout@gmail.com <xmpp:lancestout@gmail.com?message>`_,
`@lancestout <http://twitter.com/lancestout>`_
Both Fritzy and Lance work for `&yet <http://andyet.net>`_, which specializes in
realtime web and XMPP applications.
- `contact@andyet.net <mailto:contact@andyet.net>`_
- `XMPP Consulting <http://xmppconsulting.com>`_
**Contributors:**
- Brian Beggs (`macdiesel <http://github.com/macdiesel>`_)
- Dann Martens (`dannmartens <http://github.com/dannmartens>`_)
- Florent Le Coz (`louiz <http://github.com/louiz>`_)
- Kevin Smith (`Kev <http://github.com/Kev>`_, http://kismith.co.uk)
- Remko Tronçon (`remko <http://github.com/remko>`_, http://el-tramo.be)
- Te-jé Rogers (`te-je <http://github.com/te-je>`_)
- Thom Nichols (`tomstrummer <http://github.com/tomstrummer>`_)

5
docs/license.rst Normal file
View File

@@ -0,0 +1,5 @@
.. _license:
License (MIT)
=============
.. include:: ../LICENSE

170
docs/make.bat Normal file
View File

@@ -0,0 +1,170 @@
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. singlehtml to make a single large HTML file
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. text to make text files
echo. man to make manual pages
echo. changes to make an overview over all changed/added/deprecated items
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\SleekXMPP.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\SleekXMPP.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
:end

2
docs/plugin_arch.rst Normal file
View File

@@ -0,0 +1,2 @@
Plugin Architecture
===================

BIN
docs/python-objects.inv Normal file

Binary file not shown.

2
docs/sasl.rst Normal file
View File

@@ -0,0 +1,2 @@
How SASL Authentication Works
=============================

50
docs/xeps.rst Normal file
View File

@@ -0,0 +1,50 @@
Supported XEPS
==============
======= ============================= ================
XEP Description Notes
======= ============================= ================
`0004`_ Data forms
`0009`_ Jabber RPC
`0012`_ Last Activity
`0030`_ Service Discovery
`0033`_ Extended Stanza Addressing
`0045`_ Multi-User Chat (MUC) Client-side only
`0050`_ Ad-hoc Commands
`0059`_ Result Set Management
`0060`_ Publish/Subscribe (PubSub) Client-side only
`0066`_ Out-of-band Data
`0078`_ Non-SASL Authentication
`0082`_ XMPP Date and Time Profiles
`0085`_ Chat-State Notifications
`0086`_ Error Condition Mappings
`0092`_ Software Version
`0128`_ Service Discovery Extensions
`0202`_ Entity Time
`0203`_ Delayed Delivery
`0224`_ Attention
`0249`_ Direct MUC Invitations
======= ============================= ================
.. _0004: http://xmpp.org/extensions/xep-0004.html
.. _0009: http://xmpp.org/extensions/xep-0009.html
.. _0012: http://xmpp.org/extensions/xep-0012.html
.. _0030: http://xmpp.org/extensions/xep-0030.html
.. _0033: http://xmpp.org/extensions/xep-0033.html
.. _0045: http://xmpp.org/extensions/xep-0045.html
.. _0050: http://xmpp.org/extensions/xep-0050.html
.. _0059: http://xmpp.org/extensions/xep-0059.html
.. _0060: http://xmpp.org/extensions/xep-0060.html
.. _0066: http://xmpp.org/extensions/xep-0066.html
.. _0078: http://xmpp.org/extensions/xep-0078.html
.. _0082: http://xmpp.org/extensions/xep-0082.html
.. _0085: http://xmpp.org/extensions/xep-0085.html
.. _0086: http://xmpp.org/extensions/xep-0086.html
.. _0092: http://xmpp.org/extensions/xep-0092.html
.. _0128: http://xmpp.org/extensions/xep-0128.html
.. _0199: http://xmpp.org/extensions/xep-0199.html
.. _0202: http://xmpp.org/extensions/xep-0202.html
.. _0203: http://xmpp.org/extensions/xep-0203.html
.. _0224: http://xmpp.org/extensions/xep-0224.html
.. _0249: http://xmpp.org/extensions/xep-0249.html

249
docs/xmpp_tdg.rst Normal file
View File

@@ -0,0 +1,249 @@
Following *XMPP: The Definitive Guide*
======================================
SleekXMPP was featured in the first edition of the O'Reilly book
`XMPP: The Definitive Guide <http://oreilly.com/catalog/9780596521271/>`_
by Peter Saint-Andre, Kevin Smith, and Remko Tronçon. The original source code
for the book's examples can be found at http://github.com/remko/xmpp-tdg. An
updated version of the source code, maintained to stay current with the latest
SleekXMPP release, is available at http://github.com/legastero/xmpp-tdg.
However, since publication, SleekXMPP has advanced from version 0.2.1 to version
1.0 and there have been several major API changes. The most notable is the
introduction of :term:`stanza objects <stanza object>` which have simplified and
standardized interactions with the XMPP XML stream.
What follows is a walk-through of *The Definitive Guide* highlighting the
changes needed to make the code examples work with version 1.0 of SleekXMPP.
These changes have been kept to a minimum to preserve the correlation with
the book's explanations, so be aware that some code may not use current best
practices.
Example 2-2. (Page 26)
----------------------
**Implementation of a basic bot that echoes all incoming messages back to its sender.**
The echo bot example requires a change to the ``handleIncomingMessage`` method
to reflect the use of the ``Message`` :term:`stanza object`. The
``"jid"`` field of the message object should now be ``"from"`` to match the
``from`` attribute of the actual XML message stanza. Likewise, ``"message"``
changes to ``"body"`` to match the ``body`` element of the message stanza.
Updated Code
~~~~~~~~~~~~
.. code-block:: python
def handleIncomingMessage(self, message):
self.xmpp.sendMessage(message["from"], message["body"])
`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/EchoBot/EchoBot.py>`_ |
`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/EchoBot/EchoBot.py>`_
Example 14-1. (Page 215)
------------------------
**CheshiR IM bot implementation.**
The main event handling method in the Bot class is meant to process both message
events and presence update events. With the new changes in SleekXMPP 1.0,
extracting a CheshiR status "message" from both types of stanzas
requires accessing different attributes. In the case of a message stanza, the
``"body"`` attribute would contain the CheshiR message. For a presence event,
the information is stored in the ``"status"`` attribute. To handle both cases,
we can test the type of the given event object and look up the proper attribute
based on the type.
Like in the EchoBot example, the expression ``event["jid"]`` needs to change
to ``event["from"]`` in order to get a JID object for the stanza's sender.
Because other functions in CheshiR assume that the JID is a string, the ``jid``
attribute is used to access the string version of the JID. A check is also added
in case ``user`` is ``None``, but the check could (and probably should) be
placed in ``addMessageFromUser``.
Another change is needed in ``handleMessageAddedToBackend`` where
an HTML-IM response is created. The HTML content should be enclosed in a single
element, such as a ``<p>`` tag.
Updated Code
~~~~~~~~~~~~
.. code-block:: python
def handleIncomingXMPPEvent(self, event):
msgLocations = {sleekxmpp.stanza.presence.Presence: "status",
sleekxmpp.stanza.message.Message: "body"}
message = event[msgLocations[type(event)]]
user = self.backend.getUserFromJID(event["from"].jid)
if user is not None:
self.backend.addMessageFromUser(message, user)
def handleMessageAddedToBackend(self, message) :
body = message.user + ": " + message.text
htmlBody = "<p><a href='%(uri)s'>%(user)s</a>: %(message)s</p>" % {
"uri": self.url + "/" + message.user,
"user" : message.user, "message" : message.text }
for subscriberJID in self.backend.getSubscriberJIDs(message.user) :
self.xmpp.sendMessage(subscriberJID, body, mhtml=htmlBody)
`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/Bot.py>`_ |
`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/Bot.py>`_
Example 14-3. (Page 217)
------------------------
**Configurable CheshiR IM bot implementation.**
.. note::
Since the CheshiR examples build on each other, see previous sections for
corrections to code that is not marked as new in the book example.
The main difference for the configurable IM bot is the handling for the
data form in ``handleConfigurationCommand``. The test for equality
with the string ``"1"`` is no longer required; SleekXMPP converts
boolean data form fields to the values ``True`` and ``False``
automatically.
For the method ``handleIncomingXMPPPresence``, the attribute
``"jid"`` is again converted to ``"from"`` to get a JID
object for the presence stanza's sender, and the ``jid`` attribute is
used to access the string version of that JID object. A check is also added in
case ``user`` is ``None``, but the check could (and probably
should) be placed in ``getShouldMonitorPresenceFromUser``.
Updated Code
~~~~~~~~~~~~
.. code-block:: python
def handleConfigurationCommand(self, form, sessionId):
values = form.getValues()
monitorPresence =values["monitorPresence"]
jid = self.xmpp.plugin["xep_0050"].sessions[sessionId]["jid"]
user = self.backend.getUserFromJID(jid)
self.backend.setShouldMonitorPresenceFromUser(user, monitorPresence)
def handleIncomingXMPPPresence(self, event):
user = self.backend.getUserFromJID(event["from"].jid)
if user is not None:
if self.backend.getShouldMonitorPresenceFromUser(user):
self.handleIncomingXMPPEvent(event)
`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/ConfigurableBot.py>`_ |
`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/ConfigurableBot.py>`_
Example 14-4. (Page 220)
------------------------
**CheshiR IM server component implementation.**
.. note::
Since the CheshiR examples build on each other, see previous sections for
corrections to code that is not marked as new in the book example.
Like several previous examples, a needed change is to replace
``subscription["from"]`` with ``subscription["from"].jid`` because the
``BaseXMPP`` method ``makePresence`` requires the JID to be a string.
A correction needs to be made in ``handleXMPPPresenceProbe`` because a line was
left out of the original implementation; the variable ``user`` is undefined. The
JID of the user can be extracted from the presence stanza's ``from`` attribute.
Since this implementation of CheshiR uses an XMPP component, it must
include a ``from`` attribute in all messages that it sends. Adding the
``from`` attribute is done by including ``mfrom=self.xmpp.jid`` in calls to
``self.xmpp.sendMessage``.
Updated Code
~~~~~~~~~~~~
.. code-block:: python
def handleXMPPPresenceProbe(self, event) :
self.xmpp.sendPresence(pto = event["from"])
def handleXMPPPresenceSubscription(self, subscription) :
if subscription["type"] == "subscribe" :
userJID = subscription["from"].jid
self.xmpp.sendPresenceSubscription(pto=userJID, ptype="subscribed")
self.xmpp.sendPresence(pto = userJID)
self.xmpp.sendPresenceSubscription(pto=userJID, ptype="subscribe")
def handleMessageAddedToBackend(self, message) :
body = message.user + ": " + message.text
for subscriberJID in self.backend.getSubscriberJIDs(message.user) :
self.xmpp.sendMessage(subscriberJID, body, mfrom=self.xmpp.jid)
`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/SimpleComponent.py>`_ |
`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/SimpleComponent.py>`_
Example 14-6. (Page 223)
------------------------
**CheshiR IM server component with in-band registration support.**
.. note::
Since the CheshiR examples build on each other, see previous sections for
corrections to code that is not marked as new in the book example.
After applying the changes from Example 14-4 above, the registrable component
implementation should work correctly.
.. tip::
To see how to implement in-band registration as a SleekXMPP plugin,
see the tutorial :ref:`tutorial-create-plugin`.
`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/RegistrableComponent.py>`_ |
`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/RegistrableComponent.py>`_
Example 14-7. (Page 225)
------------------------
**Extended CheshiR IM server component implementation.**
.. note::
Since the CheshiR examples build on each other, see previous
sections for corrections to code that is not marked as new in the book
example.
While the final code example can look daunting with all of the changes
made, it requires very few modifications to work with the latest version of
SleekXMPP. Most differences are the result of CheshiR's backend functions
expecting JIDs to be strings so that they can be stripped to bare JIDs. To
resolve these, use the ``jid`` attribute of the JID objects. Also,
references to ``"message"`` and ``"jid"`` attributes need to
be changed to either ``"body"`` or ``"status"``, and either
``"from"`` or ``"to"`` depending on if the object is a message
or presence stanza and which of the JIDs from the stanza is needed.
Updated Code
~~~~~~~~~~~~
.. code-block:: python
def handleIncomingXMPPMessage(self, event) :
message = self.addRecipientToMessage(event["body"], event["to"].jid)
user = self.backend.getUserFromJID(event["from"].jid)
self.backend.addMessageFromUser(message, user)
def handleIncomingXMPPPresence(self, event) :
if event["to"].jid == self.componentDomain :
user = self.backend.getUserFromJID(event["from"].jid)
self.backend.addMessageFromUser(event["status"], user)
...
def handleXMPPPresenceSubscription(self, subscription) :
if subscription["type"] == "subscribe" :
userJID = subscription["from"].jid
user = self.backend.getUserFromJID(userJID)
contactJID = subscription["to"]
self.xmpp.sendPresenceSubscription(
pfrom=contactJID, pto=userJID, ptype="subscribed", pnick=user)
self.sendPresenceOfContactToUser(contactJID=contactJID, userJID=userJID)
if contactJID == self.componentDomain :
self.sendAllContactSubscriptionRequestsToUser(userJID)
`View full source <http://github.com/legastero/xmpp-tdg/blob/master/code/CheshiR/Component.py>`_ |
`View original code <http://github.com/remko/xmpp-tdg/blob/master/code/CheshiR/Component.py>`_

View File

@@ -1,48 +0,0 @@
# coding=utf8
import sleekxmpp
import logging
from optparse import OptionParser
import time
import sys
if sys.version_info < (3,0):
reload(sys)
sys.setdefaultencoding('utf8')
class Example(sleekxmpp.ClientXMPP):
def __init__(self, jid, password):
sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.add_event_handler("session_start", self.start)
self.add_event_handler("message", self.message)
def start(self, event):
self.getRoster()
self.sendPresence()
def message(self, msg):
msg.reply("Thanks for sending\n%(body)s" % msg).send()
if __name__ == '__main__':
#parse command line arguements
optp = OptionParser()
optp.add_option('-q','--quiet', help='set logging to ERROR', action='store_const', dest='loglevel', const=logging.ERROR, default=logging.INFO)
optp.add_option('-d','--debug', help='set logging to DEBUG', action='store_const', dest='loglevel', const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v','--verbose', help='set logging to COMM', action='store_const', dest='loglevel', const=5, default=logging.INFO)
optp.add_option("-c","--config", dest="configfile", default="config.xml", help="set config file to use")
opts,args = optp.parse_args()
logging.basicConfig(level=opts.loglevel, format='%(levelname)-8s %(message)s')
xmpp = Example('user@gmail.com/sleekxmpp', 'password')
xmpp.registerPlugin('xep_0004')
xmpp.registerPlugin('xep_0030')
xmpp.registerPlugin('xep_0060')
xmpp.registerPlugin('xep_0199')
if xmpp.connect(('talk.google.com', 5222)):
xmpp.process(threaded=False)
print("done")
else:
print("Unable to connect.")

204
examples/adhoc_provider.py Executable file
View File

@@ -0,0 +1,204 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import sys
import logging
import getpass
from optparse import OptionParser
import sleekxmpp
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class CommandBot(sleekxmpp.ClientXMPP):
"""
A simple SleekXMPP bot that provides a basic
adhoc command.
"""
def __init__(self, jid, password):
sleekxmpp.ClientXMPP.__init__(self, jid, password)
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
def start(self, event):
"""
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
event -- An empty dictionary. The session_start
event does not provide any additional
data.
"""
self.send_presence()
self.get_roster()
# We add the command after session_start has fired
# to ensure that the correct full JID is used.
# If using a component, may also pass jid keyword parameter.
self['xep_0050'].add_command(node='greeting',
name='Greeting',
handler=self._handle_command)
def _handle_command(self, iq, session):
"""
Respond to the initial request for a command.
Arguments:
iq -- The iq stanza containing the command request.
session -- A dictionary of data relevant to the command
session. Additional, custom data may be saved
here to persist across handler callbacks.
"""
form = self['xep_0004'].makeForm('form', 'Greeting')
form['instructions'] = 'Send a custom greeting to a JID'
form.addField(var='greeting',
ftype='text-single',
label='Your greeting')
session['payload'] = form
session['next'] = self._handle_command_complete
session['has_next'] = False
# Other useful session values:
# session['to'] -- The JID that received the
# command request.
# session['from'] -- The JID that sent the
# command request.
# session['has_next'] = True -- There are more steps to complete
# session['allow_complete'] = True -- Allow user to finish immediately
# and possibly skip steps
# session['cancel'] = handler -- Assign a handler for if the user
# cancels the command.
# session['notes'] = [ -- Add informative notes about the
# ('info', 'Info message'), command's results.
# ('warning', 'Warning message'),
# ('error', 'Error message')]
return session
def _handle_command_complete(self, payload, session):
"""
Process a command result from the user.
Arguments:
payload -- Either a single item, such as a form, or a list
of items or forms if more than one form was
provided to the user. The payload may be any
stanza, such as jabber:x:oob for out of band
data, or jabber:x:data for typical data forms.
session -- A dictionary of data relevant to the command
session. Additional, custom data may be saved
here to persist across handler callbacks.
"""
# In this case (as is typical), the payload is a form
form = payload
greeting = form['values']['greeting']
self.send_message(mto=session['from'],
mbody="%s, World!" % greeting,
mtype='chat')
# Having no return statement is the same as unsetting the 'payload'
# and 'next' session values and returning the session.
# Unless it is the final step, always return the session dictionary.
session['payload'] = None
session['next'] = None
return session
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR',
action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM',
action='store_const', dest='loglevel',
const=5, default=logging.INFO)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
opts, args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
# Setup the CommandBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = CommandBot(opts.jid, opts.password)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0050') # Adhoc Commands
xmpp.register_plugin('xep_0199', {'keepalive': True, 'frequency':15})
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

210
examples/adhoc_user.py Executable file
View File

@@ -0,0 +1,210 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import sys
import logging
import getpass
from optparse import OptionParser
import sleekxmpp
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class CommandUserBot(sleekxmpp.ClientXMPP):
"""
A simple SleekXMPP bot that uses the adhoc command
provided by the adhoc_provider.py example.
"""
def __init__(self, jid, password, other, greeting):
sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.command_provider = other
self.greeting = greeting
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
self.add_event_handler("message", self.message)
def start(self, event):
"""
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
event -- An empty dictionary. The session_start
event does not provide any additional
data.
"""
self.send_presence()
self.get_roster()
# We first create a session dictionary containing:
# 'next' -- the handler to execute on a successful response
# 'error' -- the handler to execute if an error occurs
# The session may also contain custom data.
session = {'greeting': self.greeting,
'next': self._command_start,
'error': self._command_error}
self['xep_0050'].start_command(jid=self.command_provider,
node='greeting',
session=session)
def message(self, msg):
"""
Process incoming message stanzas.
Arguments:
msg -- The received message stanza.
"""
logging.info(msg['body'])
def _command_start(self, iq, session):
"""
Process the initial command result.
Arguments:
iq -- The iq stanza containing the command result.
session -- A dictionary of data relevant to the command
session. Additional, custom data may be saved
here to persist across handler callbacks.
"""
# The greeting command provides a form with a single field:
# <x xmlns="jabber:x:data" type="form">
# <field var="greeting"
# type="text-single"
# label="Your greeting" />
# </x>
form = self['xep_0004'].makeForm(ftype='submit')
form.addField(var='greeting',
value=session['greeting'])
session['payload'] = form
# We don't need to process the next result.
session['next'] = None
# Other options include using:
# continue_command() -- Continue to the next step in the workflow
# cancel_command() -- Stop command execution.
self['xep_0050'].complete_command(session)
def _command_error(self, iq, session):
"""
Process an error that occurs during command execution.
Arguments:
iq -- The iq stanza containing the error.
session -- A dictionary of data relevant to the command
session. Additional, custom data may be saved
here to persist across handler callbacks.
"""
logging.error("COMMAND: %s %s" % (iq['error']['condition'],
iq['error']['text']))
# Terminate the command's execution and clear its session.
# The session will automatically be cleared if no error
# handler is provided.
self['xep_0050'].terminate_command(session)
self.disconnect()
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR',
action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM',
action='store_const', dest='loglevel',
const=5, default=logging.INFO)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
optp.add_option("-o", "--other", dest="other",
help="JID providing commands")
optp.add_option("-g", "--greeting", dest="greeting",
help="Greeting")
opts, args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
if opts.other is None:
opts.other = raw_input("JID Providing Commands: ")
if opts.greeting is None:
opts.greeting = raw_input("Greeting: ")
# Setup the CommandBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = CommandUserBot(opts.jid, opts.password, opts.other, opts.greeting)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0050') # Adhoc Commands
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

View File

@@ -0,0 +1,173 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import sys
import logging
import getpass
from optparse import OptionParser
import sleekxmpp
from sleekxmpp import ClientXMPP, Iq
from sleekxmpp.exceptions import IqError, IqTimeout, XMPPError
from sleekxmpp.xmlstream import register_stanza_plugin
from sleekxmpp.xmlstream.handler import Callback
from sleekxmpp.xmlstream.matcher import StanzaPath
from stanza import Action
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class ActionBot(sleekxmpp.ClientXMPP):
"""
A simple SleekXMPP bot that receives a custom stanza
from another client.
"""
def __init__(self, jid, password):
sleekxmpp.ClientXMPP.__init__(self, jid, password)
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
self.registerHandler(
Callback('Some custom iq',
StanzaPath('iq@type=set/action'),
self._handle_action))
self.add_event_handler('custom_action',
self._handle_action_event,
threaded=True)
register_stanza_plugin(Iq, Action)
def start(self, event):
"""
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
event -- An empty dictionary. The session_start
event does not provide any additional
data.
"""
self.send_presence()
self.get_roster()
def _handle_action(self, iq):
"""
Raise an event for the stanza so that it can be processed in its
own thread without blocking the main stanza processing loop.
"""
self.event('custom_action', iq)
def _handle_action_event(self, iq):
"""
Respond to the custom action event.
Since one of the actions is to disconnect, this
event handler needs to be run in threaded mode, by
using `threaded=True` in the `add_event_handler` call.
"""
method = iq['action']['method']
param = iq['action']['param']
if method == 'is_prime' and param == '2':
print("got message: %s" % iq)
iq.reply()
iq['action']['status'] = 'done'
iq.send()
elif method == 'bye':
print("got message: %s" % iq)
self.disconnect()
else:
print("got message: %s" % iq)
iq.reply()
iq['action']['status'] = 'error'
iq.send()
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR',
action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM',
action='store_const', dest='loglevel',
const=5, default=logging.INFO)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
opts, args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
# Setup the CommandBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = ActionBot(opts.jid, opts.password)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0050') # Adhoc Commands
xmpp.register_plugin('xep_0199', {'keepalive': True, 'frequency':15})
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

View File

@@ -0,0 +1,175 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import sys
import logging
import getpass
from optparse import OptionParser
import sleekxmpp
from sleekxmpp import Iq
from sleekxmpp.exceptions import XMPPError
from sleekxmpp.xmlstream import register_stanza_plugin
from stanza import Action
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class ActionUserBot(sleekxmpp.ClientXMPP):
"""
A simple SleekXMPP bot that sends a custom action stanza
to another client.
"""
def __init__(self, jid, password, other):
sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.action_provider = other
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start, threaded=True)
self.add_event_handler("message", self.message)
register_stanza_plugin(Iq, Action)
def start(self, event):
"""
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
event -- An empty dictionary. The session_start
event does not provide any additional
data.
"""
self.send_presence()
self.get_roster()
self.send_custom_iq()
def send_custom_iq(self):
"""Create and send two custom actions.
If the first action was successful, then send
a shutdown command and then disconnect.
"""
iq = self.Iq()
iq['to'] = self.action_provider
iq['type'] = 'set'
iq['action']['method'] = 'is_prime'
iq['action']['param'] = '2'
try:
resp = iq.send()
if resp['action']['status'] == 'done':
#sending bye
iq2 = self.Iq()
iq2['to'] = self.action_provider
iq2['type'] = 'set'
iq2['action']['method'] = 'bye'
iq2.send(block=False)
# The wait=True delays the disconnect until the queue
# of stanzas to be sent becomes empty.
self.disconnect(wait=True)
except XMPPError:
print('There was an error sending the custom action.')
def message(self, msg):
"""
Process incoming message stanzas.
Arguments:
msg -- The received message stanza.
"""
logging.info(msg['body'])
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR',
action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM',
action='store_const', dest='loglevel',
const=5, default=logging.INFO)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
optp.add_option("-o", "--other", dest="other",
help="JID providing custom stanza")
opts, args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
if opts.other is None:
opts.other = raw_input("JID Providing custom stanza: ")
# Setup the CommandBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = ActionUserBot(opts.jid, opts.password, opts.other)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0050') # Adhoc Commands
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

View File

@@ -0,0 +1,56 @@
from sleekxmpp.xmlstream import ElementBase
class Action(ElementBase):
"""
A stanza class for XML content of the form:
<action xmlns="sleekxmpp:custom:actions">
<method>X</method>
<param>X</param>
<status>X</status>
</action>
"""
#: The `name` field refers to the basic XML tag name of the
#: stanza. Here, the tag name will be 'action'.
name = 'action'
#: The namespace of the main XML tag.
namespace = 'sleekxmpp:custom:actions'
#: The `plugin_attrib` value is the name that can be used
#: with a parent stanza to access this stanza. For example
#: from an Iq stanza object, accessing:
#:
#: iq['action']
#:
#: would reference an Action object, and will even create
#: an Action object and append it to the Iq stanza if
#: one doesn't already exist.
plugin_attrib = 'action'
#: Stanza objects expose dictionary-like interfaces for
#: accessing and manipulating substanzas and other values.
#: The set of interfaces defined here are the names of
#: these dictionary-like interfaces provided by this stanza
#: type. For example, an Action stanza object can use:
#:
#: action['method'] = 'foo'
#: print(action['param'])
#: del action['status']
#:
#: to set, get, or remove its values.
interfaces = set(('method', 'param', 'status'))
#: By default, values in the `interfaces` set are mapped to
#: attribute values. This can be changed such that an interface
#: maps to a subelement's text value by adding interfaces to
#: the sub_interfaces set. For example, here all interfaces
#: are marked as sub_interfaces, and so the XML produced will
#: look like:
#:
#: <action xmlns="sleekxmpp:custom:actions">
#: <method>foo</method>
#: </action>
sub_interfaces = interfaces

199
examples/disco_browser.py Executable file
View File

@@ -0,0 +1,199 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import sys
import logging
import getpass
from optparse import OptionParser
import sleekxmpp
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class Disco(sleekxmpp.ClientXMPP):
"""
A demonstration for using basic service discovery.
Send a disco#info and disco#items request to a JID/node combination,
and print out the results.
May also request only particular info categories such as just features,
or just items.
"""
def __init__(self, jid, password, target_jid, target_node='', get=''):
sleekxmpp.ClientXMPP.__init__(self, jid, password)
# Using service discovery requires the XEP-0030 plugin.
self.register_plugin('xep_0030')
self.get = get
self.target_jid = target_jid
self.target_node = target_node
# Values to control which disco entities are reported
self.info_types = ['', 'all', 'info', 'identities', 'features']
self.identity_types = ['', 'all', 'info', 'identities']
self.feature_types = ['', 'all', 'info', 'features']
self.items_types = ['', 'all', 'items']
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start, threaded=True)
def start(self, event):
"""
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an initial
presence stanza.
In this case, we send disco#info and disco#items
stanzas to the requested JID and print the results.
Arguments:
event -- An empty dictionary. The session_start
event does not provide any additional
data.
"""
self.get_roster()
self.send_presence()
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.
info = self['xep_0030'].get_info(jid=self.target_jid,
node=self.target_node,
block=True)
if self.get in self.items_types:
# The same applies from above. Listen for the
# disco_items event or pass a callback function
# if you need to process a non-blocking request.
items = self['xep_0030'].get_items(jid=self.target_jid,
node=self.target_node,
block=True)
else:
logging.error("Invalid disco request type.")
self.disconnect()
return
header = 'XMPP Service Discovery: %s' % self.target_jid
print(header)
print('-' * len(header))
if self.target_node != '':
print('Node: %s' % self.target_node)
print('-' * len(header))
if self.get in self.identity_types:
print('Identities:')
for identity in info['disco_info']['identities']:
print(' - %s' % str(identity))
if self.get in self.feature_types:
print('Features:')
for feature in info['disco_info']['features']:
print(' - %s' % feature)
if self.get in self.items_types:
print('Items:')
for item in items['disco_items']['items']:
print(' - %s' % str(item))
self.disconnect()
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
optp.version = '%%prog 0.1'
optp.usage = "Usage: %%prog [options] %s <jid> [<node>]" % \
'all|info|items|identities|features'
optp.add_option('-q','--quiet', help='set logging to ERROR',
action='store_const',
dest='loglevel',
const=logging.ERROR,
default=logging.ERROR)
optp.add_option('-d','--debug', help='set logging to DEBUG',
action='store_const',
dest='loglevel',
const=logging.DEBUG,
default=logging.ERROR)
optp.add_option('-v','--verbose', help='set logging to COMM',
action='store_const',
dest='loglevel',
const=5,
default=logging.ERROR)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
opts,args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if len(args) < 2:
optp.print_help()
exit()
if len(args) == 2:
args = (args[0], args[1], '')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
# Setup the Disco browser.
xmpp = Disco(opts.jid, opts.password, args[1], args[2], args[0])
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
else:
print("Unable to connect.")

144
examples/echo_client.py Executable file
View File

@@ -0,0 +1,144 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import sys
import logging
import getpass
from optparse import OptionParser
import sleekxmpp
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class EchoBot(sleekxmpp.ClientXMPP):
"""
A simple SleekXMPP bot that will echo messages it
receives, along with a short thank you message.
"""
def __init__(self, jid, password):
sleekxmpp.ClientXMPP.__init__(self, jid, password)
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
# The message event is triggered whenever a message
# stanza is received. Be aware that that includes
# MUC messages and error messages.
self.add_event_handler("message", self.message)
def start(self, event):
"""
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
event -- An empty dictionary. The session_start
event does not provide any additional
data.
"""
self.send_presence()
self.get_roster()
def message(self, msg):
"""
Process incoming message stanzas. Be aware that this also
includes MUC messages and error messages. It is usually
a good idea to check the messages's type before processing
or sending replies.
Arguments:
msg -- The received message stanza. See the documentation
for stanza objects and the Message stanza to see
how it may be used.
"""
if msg['type'] in ('chat', 'normal'):
msg.reply("Thanks for sending\n%(body)s" % msg).send()
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR',
action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM',
action='store_const', dest='loglevel',
const=5, default=logging.INFO)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
opts, args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.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(opts.jid, opts.password)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0060') # PubSub
xmpp.register_plugin('xep_0199') # XMPP Ping
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

122
examples/echo_component.py Executable file
View File

@@ -0,0 +1,122 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import sys
import logging
import getpass
from optparse import OptionParser
import sleekxmpp
from sleekxmpp.componentxmpp import ComponentXMPP
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class EchoComponent(ComponentXMPP):
"""
A simple SleekXMPP component that echoes messages.
"""
def __init__(self, jid, secret, server, port):
ComponentXMPP.__init__(self, jid, secret, server, port)
# You don't need a session_start handler, but that is
# where you would broadcast initial presence.
# The message event is triggered whenever a message
# stanza is received. Be aware that that includes
# MUC messages and error messages.
self.add_event_handler("message", self.message)
def message(self, msg):
"""
Process incoming message stanzas. Be aware that this also
includes MUC messages and error messages. It is usually
a good idea to check the messages's type before processing
or sending replies.
Since a component may send messages from any number of JIDs,
it is best to always include a from JID.
Arguments:
msg -- The received message stanza. See the documentation
for stanza objects and the Message stanza to see
how it may be used.
"""
# The reply method will use the messages 'to' JID as the
# outgoing reply's 'from' JID.
msg.reply("Thanks for sending\n%(body)s" % msg).send()
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR',
action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM',
action='store_const', dest='loglevel',
const=5, default=logging.INFO)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
optp.add_option("-s", "--server", dest="server",
help="server to connect to")
optp.add_option("-P", "--port", dest="port",
help="port to connect to")
opts, args = optp.parse_args()
if opts.jid is None:
opts.jid = raw_input("Component JID: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
if opts.server is None:
opts.server = raw_input("Server: ")
if opts.port is None:
opts.port = int(raw_input("Port: "))
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
# Setup the EchoComponent and register plugins. Note that while plugins
# may have interdependencies, the order in which you register them does
# not matter.
xmpp = EchoComponent(opts.jid, opts.password, opts.server, opts.port)
xmpp.registerPlugin('xep_0030') # Service Discovery
xmpp.registerPlugin('xep_0004') # Data Forms
xmpp.registerPlugin('xep_0060') # PubSub
xmpp.registerPlugin('xep_0199') # XMPP Ping
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

193
examples/muc.py Executable file
View File

@@ -0,0 +1,193 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import sys
import logging
import getpass
from optparse import OptionParser
import sleekxmpp
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class MUCBot(sleekxmpp.ClientXMPP):
"""
A simple SleekXMPP bot that will greets those
who enter the room, and acknowledge any messages
that mentions the bot's nickname.
"""
def __init__(self, jid, password, room, nick):
sleekxmpp.ClientXMPP.__init__(self, jid, password)
self.room = room
self.nick = nick
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
# The groupchat_message event is triggered whenever a message
# stanza is received from any chat room. If you also also
# register a handler for the 'message' event, MUC messages
# will be processed by both handlers.
self.add_event_handler("groupchat_message", self.muc_message)
# The groupchat_presence event is triggered whenever a
# presence stanza is received from any chat room, including
# any presences you send yourself. To limit event handling
# to a single room, use the events muc::room@server::presence,
# muc::room@server::got_online, or muc::room@server::got_offline.
self.add_event_handler("muc::%s::got_online" % self.room,
self.muc_online)
def start(self, event):
"""
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
event -- An empty dictionary. The session_start
event does not provide any additional
data.
"""
self.get_roster()
self.send_presence()
self.plugin['xep_0045'].joinMUC(self.room,
self.nick,
# If a room password is needed, use:
# password=the_room_password,
wait=True)
def muc_message(self, msg):
"""
Process incoming message stanzas from any chat room. Be aware
that if you also have any handlers for the 'message' event,
message stanzas may be processed by both handlers, so check
the 'type' attribute when using a 'message' event handler.
Whenever the bot's nickname is mentioned, respond to
the message.
IMPORTANT: Always check that a message is not from yourself,
otherwise you will create an infinite loop responding
to your own messages.
This handler will reply to messages that mention
the bot's nickname.
Arguments:
msg -- The received message stanza. See the documentation
for stanza objects and the Message stanza to see
how it may be used.
"""
if msg['mucnick'] != self.nick and self.nick in msg['body']:
self.send_message(mto=msg['from'].bare,
mbody="I heard that, %s." % msg['mucnick'],
mtype='groupchat')
def muc_online(self, presence):
"""
Process a presence stanza from a chat room. In this case,
presences from users that have just come online are
handled by sending a welcome message that includes
the user's nickname and role in the room.
Arguments:
presence -- The received presence stanza. See the
documentation for the Presence stanza
to see how else it may be used.
"""
if presence['muc']['nick'] != self.nick:
self.send_message(mto=presence['from'].bare,
mbody="Hello, %s %s" % (presence['muc']['role'],
presence['muc']['nick']),
mtype='groupchat')
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR',
action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM',
action='store_const', dest='loglevel',
const=5, default=logging.INFO)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
optp.add_option("-r", "--room", dest="room",
help="MUC room to join")
optp.add_option("-n", "--nick", dest="nick",
help="MUC nickname")
opts, args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
if opts.room is None:
opts.room = raw_input("MUC room: ")
if opts.nick is None:
opts.nick = raw_input("MUC nickname: ")
# Setup the MUCBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = MUCBot(opts.jid, opts.password, opts.room, opts.nick)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0045') # Multi-User Chat
xmpp.register_plugin('xep_0199') # XMPP Ping
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

141
examples/ping.py Executable file
View File

@@ -0,0 +1,141 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import sys
import logging
import getpass
from optparse import OptionParser
import sleekxmpp
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class PingTest(sleekxmpp.ClientXMPP):
"""
A simple SleekXMPP bot that will send a ping request
to a given JID.
"""
def __init__(self, jid, password, pingjid):
sleekxmpp.ClientXMPP.__init__(self, jid, password)
if pingjid is None:
pingjid = self.jid
self.pingjid = pingjid
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start, threaded=True)
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()
result = self['xep_0199'].send_ping(self.pingjid,
timeout=10,
errorfalse=True)
logging.info("Pinging...")
if result is False:
logging.info("Couldn't ping.")
self.disconnect()
sys.exit(1)
else:
logging.info("Success! RTT: %s", str(result))
self.disconnect()
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR',
action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM',
action='store_const', dest='loglevel',
const=5, default=logging.INFO)
optp.add_option('-t', '--pingto', help='set jid to ping',
action='store', type='string', dest='pingjid',
default=None)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
opts, args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
# Setup the PingTest and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = PingTest(opts.jid, opts.password, opts.pingjid)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0060') # PubSub
xmpp.register_plugin('xep_0199') # XMPP Ping
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

168
examples/proxy_echo_client.py Executable file
View File

@@ -0,0 +1,168 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import sys
import logging
import getpass
from optparse import OptionParser
import sleekxmpp
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class EchoBot(sleekxmpp.ClientXMPP):
"""
A simple SleekXMPP bot that will echo messages it
receives, along with a short thank you message.
"""
def __init__(self, jid, password):
sleekxmpp.ClientXMPP.__init__(self, jid, password)
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
# The message event is triggered whenever a message
# stanza is received. Be aware that that includes
# MUC messages and error messages.
self.add_event_handler("message", self.message)
def start(self, event):
"""
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
event -- An empty dictionary. The session_start
event does not provide any additional
data.
"""
self.send_presence()
self.get_roster()
def message(self, msg):
"""
Process incoming message stanzas. Be aware that this also
includes MUC messages and error messages. It is usually
a good idea to check the messages's type before processing
or sending replies.
Arguments:
msg -- The received message stanza. See the documentation
for stanza objects and the Message stanza to see
how it may be used.
"""
msg.reply("Thanks for sending\n%(body)s" % msg).send()
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR',
action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM',
action='store_const', dest='loglevel',
const=5, default=logging.INFO)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
optp.add_option("--phost", dest="proxy_host",
help="Proxy hostname")
optp.add_option("--pport", dest="proxy_port",
help="Proxy port")
optp.add_option("--puser", dest="proxy_user",
help="Proxy username")
optp.add_option("--ppass", dest="proxy_pass",
help="Proxy password")
opts, args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
if opts.proxy_host is None:
opts.proxy_host = raw_input("Proxy host: ")
if opts.proxy_port is None:
opts.proxy_port = raw_input("Proxy port: ")
if opts.proxy_user is None:
opts.proxy_user = raw_input("Proxy username: ")
if opts.proxy_pass is None and opts.proxy_user:
opts.proxy_pass = getpass.getpass("Proxy password: ")
# Setup the EchoBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = EchoBot(opts.jid, opts.password)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0060') # PubSub
xmpp.register_plugin('xep_0199') # XMPP Ping
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
xmpp.use_proxy = True
xmpp.proxy_config = {
'host': opts.proxy_host,
'port': int(opts.proxy_port),
'username': opts.proxy_user,
'password': opts.proxy_pass}
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

198
examples/pubsub_client.py Normal file
View File

@@ -0,0 +1,198 @@
import sys
import logging
import getpass
from optparse import OptionParser
import sleekxmpp
from sleekxmpp.xmlstream import ET, tostring
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class PubsubClient(sleekxmpp.ClientXMPP):
def __init__(self, jid, password, server,
node=None, action='list', data=''):
super(PubsubClient, self).__init__(jid, password)
self.register_plugin('xep_0030')
self.register_plugin('xep_0059')
self.register_plugin('xep_0060')
self.actions = ['nodes', 'create', 'delete',
'publish', 'get', 'retract',
'purge', 'subscribe', 'unsubscribe']
self.action = action
self.node = node
self.data = data
self.pubsub_server = server
self.add_event_handler('session_start', self.start, threaded=True)
def start(self, event):
self.get_roster()
self.send_presence()
try:
getattr(self, self.action)()
except:
logging.error('Could not execute: %s' % self.action)
self.disconnect()
def nodes(self):
try:
result = self['xep_0060'].get_nodes(self.pubsub_server, self.node)
for item in result['disco_items']['items']:
print(' - %s' % str(item))
except:
logging.error('Could not retrieve node list.')
def create(self):
try:
self['xep_0060'].create_node(self.pubsub_server, self.node)
except:
logging.error('Could not create node: %s' % self.node)
def delete(self):
try:
self['xep_0060'].delete_node(self.pubsub_server, self.node)
print('Deleted node: %s' % self.node)
except:
logging.error('Could not delete node: %s' % self.node)
def publish(self):
payload = ET.fromstring("<test xmlns='test'>%s</test>" % self.data)
try:
result = self['xep_0060'].publish(self.pubsub_server, self.node, payload=payload)
id = result['pubsub']['publish']['item']['id']
print('Published at item id: %s' % id)
except:
logging.error('Could not publish to: %s' % self.node)
def get(self):
try:
result = self['xep_0060'].get_item(self.pubsub_server, self.node, self.data)
for item in result['pubsub']['items']['substanzas']:
print('Retrieved item %s: %s' % (item['id'], tostring(item['payload'])))
except:
logging.error('Could not retrieve item %s from node %s' % (self.data, self.node))
def retract(self):
try:
result = self['xep_0060'].retract(self.pubsub_server, self.node, self.data)
print('Retracted item %s from node %s' % (self.data, self.node))
except:
logging.error('Could not retract item %s from node %s' % (self.data, self.node))
def purge(self):
try:
result = self['xep_0060'].purge(self.pubsub_server, self.node)
print('Purged all items from node %s' % self.node)
except:
logging.error('Could not purge items from node %s' % self.node)
def subscribe(self):
try:
result = self['xep_0060'].subscribe(self.pubsub_server, self.node)
print('Subscribed %s to node %s' % (self.boundjid.bare, self.node))
except:
logging.error('Could not subscribe %s to node %s' % (self.boundjid.bare, self.node))
def unsubscribe(self):
try:
result = self['xep_0060'].unsubscribe(self.pubsub_server, self.node)
print('Unsubscribed %s from node %s' % (self.boundjid.bare, self.node))
except:
logging.error('Could not unsubscribe %s from node %s' % (self.boundjid.bare, self.node))
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
optp.version = '%%prog 0.1'
optp.usage = "Usage: %%prog [options] <jid> " + \
'nodes|create|delete|purge|subscribe|unsubscribe|publish|retract|get' + \
' [<node> <data>]'
optp.add_option('-q','--quiet', help='set logging to ERROR',
action='store_const',
dest='loglevel',
const=logging.ERROR,
default=logging.ERROR)
optp.add_option('-d','--debug', help='set logging to DEBUG',
action='store_const',
dest='loglevel',
const=logging.DEBUG,
default=logging.ERROR)
optp.add_option('-v','--verbose', help='set logging to COMM',
action='store_const',
dest='loglevel',
const=5,
default=logging.ERROR)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
opts,args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if len(args) < 2:
optp.print_help()
exit()
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
if len(args) == 2:
args = (args[0], args[1], '', '', '')
elif len(args) == 3:
args = (args[0], args[1], args[2], '', '')
elif len(args) == 4:
args = (args[0], args[1], args[2], args[3], '')
# Setup the Pubsub client
xmpp = PubsubClient(opts.jid, opts.password,
server=args[0],
node=args[2],
action=args[1],
data=args[3])
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
else:
print("Unable to connect.")

151
examples/pubsub_events.py Normal file
View File

@@ -0,0 +1,151 @@
import sys
import logging
import getpass
from optparse import OptionParser
import sleekxmpp
from sleekxmpp.xmlstream import ET, tostring
from sleekxmpp.xmlstream.matcher import StanzaPath
from sleekxmpp.xmlstream.handler import Callback
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class PubsubEvents(sleekxmpp.ClientXMPP):
def __init__(self, jid, password):
super(PubsubEvents, self).__init__(jid, password)
self.register_plugin('xep_0030')
self.register_plugin('xep_0059')
self.register_plugin('xep_0060')
self.add_event_handler('session_start', self.start)
# Some services may require configuration to allow
# sending delete, configuration, or subscription events.
self.add_event_handler('pubsub_publish', self._publish)
self.add_event_handler('pubsub_retract', self._retract)
self.add_event_handler('pubsub_purge', self._purge)
self.add_event_handler('pubsub_delete', self._delete)
self.add_event_handler('pubsub_config', self._config)
self.add_event_handler('pubsub_subscription', self._subscription)
# Want to use nicer, more specific pubsub event names?
# self['xep_0060'].map_node_event('node_name', 'event_prefix')
# self.add_event_handler('event_prefix_publish', handler)
# self.add_event_handler('event_prefix_retract', handler)
# self.add_event_handler('event_prefix_purge', handler)
# self.add_event_handler('event_prefix_delete', handler)
def start(self, event):
self.get_roster()
self.send_presence()
def _publish(self, msg):
"""Handle receiving a publish item event."""
print('Published item %s to %s:' % (
msg['pubsub_event']['items']['item']['id'],
msg['pubsub_event']['items']['node']))
data = msg['pubsub_event']['items']['item']['payload']
if data is not None:
print(tostring(data))
else:
print('No item content')
def _retract(self, msg):
"""Handle receiving a retract item event."""
print('Retracted item %s from %s' % (
msg['pubsub_event']['items']['retract']['id'],
msg['pubsub_event']['items']['node']))
def _purge(self, msg):
"""Handle receiving a node purge event."""
print('Purged all items from %s' % (
msg['pubsub_event']['purge']['node']))
def _delete(self, msg):
"""Handle receiving a node deletion event."""
print('Deleted node %s' % (
msg['pubsub_event']['delete']['node']))
def _config(self, msg):
"""Handle receiving a node configuration event."""
print('Configured node %s:' % (
msg['pubsub_event']['configuration']['node']))
print(msg['pubsub_event']['configuration']['form'])
def _subscription(self, msg):
"""Handle receiving a node subscription event."""
print('Subscription change for node %s:' % (
msg['pubsub_event']['subscription']['node']))
print(msg['pubsub_event']['subscription'])
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR',
action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM',
action='store_const', dest='loglevel',
const=5, default=logging.INFO)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
opts, args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
logging.info("Run this in conjunction with the pubsub_client.py " + \
"example to watch events happen as you give commands.")
# Setup the PubsubEvents listener
xmpp = PubsubEvents(opts.jid, opts.password)
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

View File

@@ -0,0 +1,175 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import sys
import logging
import getpass
from optparse import OptionParser
import sleekxmpp
from sleekxmpp.exceptions import IqError, IqTimeout
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class RegisterBot(sleekxmpp.ClientXMPP):
"""
A basic bot that will attempt to register an account
with an XMPP server.
NOTE: This follows the very basic registration workflow
from XEP-0077. More advanced server registration
workflows will need to check for data forms, etc.
"""
def __init__(self, jid, password):
sleekxmpp.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, threaded=True)
# The register event provides an Iq result stanza with
# a registration form from the server. This may include
# the basic registration fields, a data form, an
# out-of-band URL, or any combination. For more advanced
# cases, you will need to examine the fields provided
# and respond accordingly. SleekXMPP provides plugins
# for data forms and OOB links that will make that easier.
self.add_event_handler("register", self.register, threaded=True)
def start(self, event):
"""
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
event -- An empty dictionary. The session_start
event does not provide any additional
data.
"""
self.send_presence()
self.get_roster()
# We're only concerned about registering, so nothing more to do here.
self.disconnect()
def register(self, iq):
"""
Fill out and submit a registration form.
The form may be composed of basic registration fields, a data form,
an out-of-band link, or any combination thereof. Data forms and OOB
links can be checked for as so:
if iq.match('iq/register/form'):
# do stuff with data form
# iq['register']['form']['fields']
if iq.match('iq/register/oob'):
# do stuff with OOB URL
# iq['register']['oob']['url']
To get the list of basic registration fields, you can use:
iq['register']['fields']
"""
resp = self.Iq()
resp['type'] = 'set'
resp['register']['username'] = self.boundjid.user
resp['register']['password'] = self.password
try:
resp.send(now=True)
logging.info("Account created for %s!" % self.boundjid)
except IqError as e:
logging.error("Could not register account: %s" %
e.iq['error']['text'])
self.disconnect()
except IqTimeout:
logging.error("No response from server.")
self.disconnect()
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR',
action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM',
action='store_const', dest='loglevel',
const=5, default=logging.INFO)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
opts, args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
# Setup the RegisterBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = RegisterBot(opts.jid, opts.password)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data forms
xmpp.register_plugin('xep_0066') # Out-of-band Data
xmpp.register_plugin('xep_0077') # In-band Registration
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

172
examples/roster_browser.py Normal file
View File

@@ -0,0 +1,172 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import sys
import logging
import getpass
import threading
from optparse import OptionParser
import sleekxmpp
from sleekxmpp.exceptions import IqError, IqTimeout
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class RosterBrowser(sleekxmpp.ClientXMPP):
"""
A basic script for dumping a client's roster to
the command line.
"""
def __init__(self, jid, password):
sleekxmpp.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. We need threaded=True so that the
# session_start handler doesn't block event processing
# while we wait for presence stanzas to arrive.
self.add_event_handler("session_start", self.start, threaded=True)
self.add_event_handler("changed_status", self.wait_for_presences)
self.received = set()
self.presences_received = threading.Event()
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:
self.get_roster()
except IqError as err:
print('Error: %' % err.iq['error']['condition'])
except IqTimeout:
print('Error: Request timed out')
self.send_presence()
print('Waiting for presence updates...\n')
self.presences_received.wait(5)
print('Roster for %s' % self.boundjid.bare)
groups = self.client_roster.groups()
for group in groups:
print('\n%s' % group)
print('-' * 72)
for jid in groups[group]:
sub = self.client_roster[jid]['subscription']
name = self.client_roster[jid]['name']
if self.client_roster[jid]['name']:
print(' %s (%s) [%s]' % (name, jid, sub))
else:
print(' %s [%s]' % (jid, sub))
connections = self.client_roster.presence(jid)
for res, pres in connections.items():
show = 'available'
if pres['show']:
show = pres['show']
print(' - %s (%s)' % (res, show))
if pres['status']:
print(' %s' % pres['status'])
self.disconnect()
def wait_for_presences(self, pres):
"""
Track how many roster entries have received presence updates.
"""
self.received.add(pres['from'].bare)
if len(self.received) >= len(self.client_roster.keys()):
self.presences_received.set()
else:
self.presences_received.clear()
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
optp.add_option('-q','--quiet', help='set logging to ERROR',
action='store_const',
dest='loglevel',
const=logging.ERROR,
default=logging.ERROR)
optp.add_option('-d','--debug', help='set logging to DEBUG',
action='store_const',
dest='loglevel',
const=logging.DEBUG,
default=logging.ERROR)
optp.add_option('-v','--verbose', help='set logging to COMM',
action='store_const',
dest='loglevel',
const=5,
default=logging.ERROR)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
opts,args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
xmpp = RosterBrowser(opts.jid, opts.password)
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
else:
print("Unable to connect.")

44
examples/rpc_async.py Normal file
View File

@@ -0,0 +1,44 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Dann Martens
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
from sleekxmpp.plugins.xep_0009.remote import Endpoint, remote, Remote, \
ANY_ALL, Future
import time
class Boomerang(Endpoint):
def FQN(self):
return 'boomerang'
@remote
def throw(self):
print "Duck!"
def main():
session = Remote.new_session('kangaroo@xmpp.org/rpc', '*****')
session.new_handler(ANY_ALL, Boomerang)
boomerang = session.new_proxy('kangaroo@xmpp.org/rpc', Boomerang)
callback = Future()
boomerang.async(callback).throw()
time.sleep(10)
session.close()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,53 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Dann Martens
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
from sleekxmpp.plugins.xep_0009.remote import Endpoint, remote, Remote, \
ANY_ALL
import threading
import time
class Thermostat(Endpoint):
def FQN(self):
return 'thermostat'
def __init__(self, initial_temperature):
self._temperature = initial_temperature
self._event = threading.Event()
@remote
def set_temperature(self, temperature):
return NotImplemented
@remote
def get_temperature(self):
return NotImplemented
@remote(False)
def release(self):
return NotImplemented
def main():
session = Remote.new_session('operator@xmpp.org/rpc', '*****')
thermostat = session.new_proxy('thermostat@xmpp.org/rpc', Thermostat)
print("Current temperature is %s" % thermostat.get_temperature())
thermostat.set_temperature(20)
time.sleep(10)
session.close()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,52 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Dann Martens
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
from sleekxmpp.plugins.xep_0009.remote import Endpoint, remote, Remote, \
ANY_ALL
import threading
class Thermostat(Endpoint):
def FQN(self):
return 'thermostat'
def __init__(self, initial_temperature):
self._temperature = initial_temperature
self._event = threading.Event()
@remote
def set_temperature(self, temperature):
print("Setting temperature to %s" % temperature)
self._temperature = temperature
@remote
def get_temperature(self):
return self._temperature
@remote(False)
def release(self):
self._event.set()
def wait_for_release(self):
self._event.wait()
def main():
session = Remote.new_session('sleek@xmpp.org/rpc', '*****')
thermostat = session.new_handler(ANY_ALL, Thermostat, 18)
thermostat.wait_for_release()
session.close()
if __name__ == '__main__':
main()

143
examples/send_client.py Executable file
View File

@@ -0,0 +1,143 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import sys
import logging
import getpass
from optparse import OptionParser
import sleekxmpp
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class SendMsgBot(sleekxmpp.ClientXMPP):
"""
A basic SleekXMPP bot that will log in, send a message,
and then log out.
"""
def __init__(self, jid, password, recipient, message):
sleekxmpp.ClientXMPP.__init__(self, jid, password)
# The message we wish to send, and the JID that
# will receive it.
self.recipient = recipient
self.msg = message
# 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, threaded=True)
def start(self, event):
"""
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
event -- An empty dictionary. The session_start
event does not provide any additional
data.
"""
self.send_presence()
self.get_roster()
self.send_message(mto=self.recipient,
mbody=self.msg,
mtype='chat')
# Using wait=True ensures that the send queue will be
# emptied before ending the session.
self.disconnect(wait=True)
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR',
action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM',
action='store_const', dest='loglevel',
const=5, default=logging.INFO)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
optp.add_option("-t", "--to", dest="to",
help="JID to send the message to")
optp.add_option("-m", "--message", dest="message",
help="message to send")
opts, args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
if opts.to is None:
opts.to = raw_input("Send To: ")
if opts.message is None:
opts.message = raw_input("Message: ")
# Setup the EchoBot and register plugins. Note that while plugins may
# have interdependencies, the order in which you register them does
# not matter.
xmpp = SendMsgBot(opts.jid, opts.password, opts.to, opts.message)
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0199') # XMPP Ping
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

247
examples/thirdparty_auth.py Normal file
View File

@@ -0,0 +1,247 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import sys
import logging
import getpass
from optparse import OptionParser
try:
from httplib import HTTPSConnection
from urllib import urlencode
except ImportError:
from urllib.parse import urlencode
from http.client import HTTPSConnection
import sleekxmpp
from sleekxmpp.xmlstream import JID
# Python versions before 3.0 do not use UTF-8 encoding
# by default. To ensure that Unicode is handled properly
# throughout SleekXMPP, we will set the default encoding
# ourselves to UTF-8.
if sys.version_info < (3, 0):
reload(sys)
sys.setdefaultencoding('utf8')
else:
raw_input = input
class ThirdPartyAuthBot(sleekxmpp.ClientXMPP):
"""
A simple SleekXMPP bot that will echo messages it
receives, along with a short thank you message.
This version uses a thirdpary service for authentication,
such as Facebook or Google.
"""
def __init__(self, jid, password):
sleekxmpp.ClientXMPP.__init__(self, jid, password)
# The X-GOOGLE-TOKEN mech is ranked lower than PLAIN
# due to Google only allowing a single SASL attempt per
# connection. So PLAIN will be used for TLS connections,
# and X-GOOGLE-TOKEN for non-TLS connections. To use
# X-GOOGLE-TOKEN with a TLS connection, explicitly select
# it using:
#
# sleekxmpp.ClientXMPP.__init__(self, jid, password,
# sasl_mech="X-GOOGLE-TOKEN")
# The session_start event will be triggered when
# the bot establishes its connection with the server
# and the XML streams are ready for use. We want to
# listen for this event so that we we can initialize
# our roster.
self.add_event_handler("session_start", self.start)
# The message event is triggered whenever a message
# stanza is received. Be aware that that includes
# MUC messages and error messages.
self.add_event_handler("message", self.message)
def start(self, event):
"""
Process the session_start event.
Typical actions for the session_start event are
requesting the roster and broadcasting an initial
presence stanza.
Arguments:
event -- An empty dictionary. The session_start
event does not provide any additional
data.
"""
self.send_presence()
self.get_roster()
def message(self, msg):
"""
Process incoming message stanzas. Be aware that this also
includes MUC messages and error messages. It is usually
a good idea to check the messages's type before processing
or sending replies.
Arguments:
msg -- The received message stanza. See the documentation
for stanza objects and the Message stanza to see
how it may be used.
"""
if msg['type'] in ('chat', 'normal'):
msg.reply("Thanks for sending\n%(body)s" % msg).send()
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR',
action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM',
action='store_const', dest='loglevel',
const=5, default=logging.INFO)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
opts, args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
access_token = None
# Since documentation on how to work with Google tokens
# can be difficult to find, we'll demo a basic version
# here. Note that responses could refer to a Captcha
# URL that would require a browser.
# Using Facebook or MSN's custom authentication requires
# a browser, but the process is the same once a token
# has been retrieved.
# Request an access token from Google:
try:
conn = HTTPSConnection('www.google.com')
except:
print('Could not connect to Google')
sys.exit()
params = urlencode({
'accountType': 'GOOGLE',
'service': 'mail',
'Email': JID(opts.jid).bare,
'Passwd': opts.password
})
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
try:
conn.request('POST', '/accounts/ClientLogin', params, headers)
resp = conn.getresponse().read()
data = {}
for line in resp.split():
k, v = line.split(b'=', 1)
data[k] = v
except Exception as e:
print('Could not retrieve login data')
sys.exit()
if b'SID' not in data:
print('Required data not found')
sys.exit()
params = urlencode({
'SID': data[b'SID'],
'LSID': data[b'LSID'],
'service': 'mail'
})
try:
conn.request('POST', '/accounts/IssueAuthToken', params, headers)
resp = conn.getresponse()
data = resp.read().split()
except:
print('Could not retrieve auth data')
sys.exit()
if not data:
print('Could not retrieve token')
sys.exit()
access_token = data[0]
# Setup the ThirdPartyAuthBot and register plugins. Note that while plugins
# may have interdependencies, the order in which you register them does not
# matter.
# If using MSN, the JID should be "user@messenger.live.com", which will
# be overridden on session bind.
# We're using an access token instead of a password, so we'll use `''` as
# a password argument filler.
xmpp = ThirdPartyAuthBot(opts.jid, '')
xmpp.credentials['access_token'] = access_token
# The credentials dictionary is used to provide additional authentication
# information beyond just a password.
xmpp.register_plugin('xep_0030') # Service Discovery
xmpp.register_plugin('xep_0004') # Data Forms
xmpp.register_plugin('xep_0060') # PubSub
# MSN will kill connections that have been inactive for even
# short periods of time. So use pings to keep the session alive;
# whitespace keepalives do not work.
xmpp.register_plugin('xep_0199', {'keepalive': True, 'frequency': 60})
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas.
# Google only allows one SASL attempt per connection, so in order to
# enable the X-GOOGLE-TOKEN mechanism, we'll disable TLS.
if xmpp.connect(use_tls=False):
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

125
examples/user_location.py Normal file
View File

@@ -0,0 +1,125 @@
#!/usr/bin/env python
import sys
import logging
import getpass
from optparse import OptionParser
try:
import json
except ImportError:
import simplejson as json
try:
import requests
except ImportError:
print('This demo requires the requests package for using HTTP.')
sys.exit()
from sleekxmpp import ClientXMPP
class LocationBot(ClientXMPP):
def __init__(self, jid, password):
super(LocationBot, self).__init__(jid, password)
self.add_event_handler('session_start', self.start, threaded=True)
self.add_event_handler('user_location_publish',
self.user_location_publish)
self.register_plugin('xep_0004')
self.register_plugin('xep_0030')
self.register_plugin('xep_0060')
self.register_plugin('xep_0115')
self.register_plugin('xep_0128')
self.register_plugin('xep_0163')
self.register_plugin('xep_0080')
self.current_tune = None
def start(self, event):
self.send_presence()
self.get_roster()
self['xep_0115'].update_caps()
print("Using freegeoip.net to get geolocation.")
r = requests.get('http://freegeoip.net/json/')
try:
data = json.loads(r.text)
except:
print("Could not retrieve user location.")
self.disconnect()
return
self['xep_0080'].publish_location(
lat=data['latitude'],
lon=data['longitude'],
locality=data['city'],
region=data['region_name'],
country=data['country_name'],
countrycode=data['country_code'],
postalcode=data['zipcode'])
def user_location_publish(self, msg):
geo = msg['pubsub_event']['items']['item']['geoloc']
print("%s is at:" % msg['from'])
for key, val in geo.values.items():
if val:
print(" %s: %s" % (key, val))
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR',
action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM',
action='store_const', dest='loglevel',
const=5, default=logging.INFO)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
opts, args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
xmpp = LocationBot(opts.jid, opts.password)
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

137
examples/user_tune.py Normal file
View File

@@ -0,0 +1,137 @@
#!/usr/bin/env python
import sys
import logging
import getpass
from optparse import OptionParser
try:
from appscript import *
except ImportError:
print('This demo requires the appscript package to interact with iTunes.')
sys.exit()
from sleekxmpp import ClientXMPP
class TuneBot(ClientXMPP):
def __init__(self, jid, password):
super(TuneBot, self).__init__(jid, password)
# Check for the current song every 5 seconds.
self.schedule('Check Current Tune', 5, self._update_tune, repeat=True)
self.add_event_handler('session_start', self.start)
self.add_event_handler('user_tune_publish', self.user_tune_publish)
self.register_plugin('xep_0004')
self.register_plugin('xep_0030')
self.register_plugin('xep_0060')
self.register_plugin('xep_0115')
self.register_plugin('xep_0118')
self.register_plugin('xep_0128')
self.register_plugin('xep_0163')
self.current_tune = None
def start(self, event):
self.send_presence()
self.get_roster()
self['xep_0115'].update_caps()
def _update_tune(self):
itunes_count = app('System Events').processes[its.name == 'iTunes'].count()
if itunes_count > 0:
iTunes = app('iTunes')
if iTunes.player_state.get() == k.playing:
track = iTunes.current_track.get()
length = track.time.get()
if ':' in length:
minutes, secs = map(int, length.split(':'))
secs += minutes * 60
else:
secs = int(length)
artist = track.artist.get()
title = track.name.get()
source = track.album.get()
rating = track.rating.get() / 10
tune = (artist, secs, rating, source, title)
if tune != self.current_tune:
self.current_tune = tune
# We have a new song playing, so publish it.
self['xep_0118'].publish_tune(
artist=artist,
length=secs,
title=title,
rating=rating,
source=source)
else:
# No song is playing, clear the user tune.
tune = None
if tune != self.current_tune:
self.current_tune = tune
self['xep_0118'].stop()
def user_tune_publish(self, msg):
tune = msg['pubsub_event']['items']['item']['tune']
print("%s is listening to: %s" % (msg['from'], tune['title']))
if __name__ == '__main__':
# Setup the command line arguments.
optp = OptionParser()
# Output verbosity options.
optp.add_option('-q', '--quiet', help='set logging to ERROR',
action='store_const', dest='loglevel',
const=logging.ERROR, default=logging.INFO)
optp.add_option('-d', '--debug', help='set logging to DEBUG',
action='store_const', dest='loglevel',
const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v', '--verbose', help='set logging to COMM',
action='store_const', dest='loglevel',
const=5, default=logging.INFO)
# JID and password options.
optp.add_option("-j", "--jid", dest="jid",
help="JID to use")
optp.add_option("-p", "--password", dest="password",
help="password to use")
opts, args = optp.parse_args()
# Setup logging.
logging.basicConfig(level=opts.loglevel,
format='%(levelname)-8s %(message)s')
if opts.jid is None:
opts.jid = raw_input("Username: ")
if opts.password is None:
opts.password = getpass.getpass("Password: ")
xmpp = TuneBot(opts.jid, opts.password)
# If you are working with an OpenFire server, you may need
# to adjust the SSL version used:
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
# If you want to verify the SSL certificates offered by a server:
# xmpp.ca_certs = "path/to/ca/cert"
# Connect to the XMPP server and start processing XMPP stanzas.
if xmpp.connect():
# If you do not have the dnspython library installed, you will need
# to manually specify the name of the server if it does not match
# the one in the JID. For example, to use Google Talk you would
# need to use:
#
# if xmpp.connect(('talk.google.com', 5222)):
# ...
xmpp.process(block=True)
print("Done")
else:
print("Unable to connect.")

183
setup.py Normal file → Executable file
View File

@@ -1,65 +1,118 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2008 Nathanael C. Fritz
# All Rights Reserved
#
# This software is licensed as described in the README file,
# which you should have received as part of this distribution.
#
# from ez_setup import use_setuptools
from distutils.core import setup
import sys
# if 'cygwin' in sys.platform.lower():
# min_version = '0.6c6'
# else:
# min_version = '0.6a9'
#
# try:
# use_setuptools(min_version=min_version)
# except TypeError:
# # locally installed ez_setup won't have min_version
# use_setuptools()
#
# from setuptools import setup, find_packages, Extension, Feature
VERSION = '0.2.3.1'
DESCRIPTION = 'SleekXMPP is an elegant Python library for XMPP (aka Jabber, Google Talk, etc).'
LONG_DESCRIPTION = """
SleekXMPP is an elegant Python library for XMPP (aka Jabber, Google Talk, etc).
"""
CLASSIFIERS = [ 'Intended Audience :: Developers',
'License :: OSI Approved :: MIT',
'Programming Language :: Python',
'Topic :: Software Development :: Libraries :: Python Modules',
]
packages = [ 'sleekxmpp',
'sleekxmpp/plugins',
'sleekxmpp/stanza',
'sleekxmpp/xmlstream',
'sleekxmpp/xmlstream/matcher',
'sleekxmpp/xmlstream/handler' ]
if sys.version_info < (3, 0):
packages.append('sleekxmpp/xmlstream/tostring26')
else:
packages.append('sleekxmpp/xmlstream/tostring')
setup(
name = "sleekxmpp",
version = VERSION,
description = DESCRIPTION,
long_description = LONG_DESCRIPTION,
author = 'Nathanael Fritz',
author_email = 'fritzy [at] netflint.net',
url = 'http://code.google.com/p/sleekxmpp',
license = 'MIT',
platforms = [ 'any' ],
packages = packages,
requires = [ 'tlslite', 'pythondns' ],
)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007-2011 Nathanael C. Fritz
# All Rights Reserved
#
# This software is licensed as described in the README.rst and LICENSE
# file, which you should have received as part of this distribution.
import sys
import codecs
try:
from setuptools import setup, Command
except ImportError:
from distutils.core import setup, Command
# from ez_setup import use_setuptools
from testall import TestCommand
from sleekxmpp.version import __version__
# if 'cygwin' in sys.platform.lower():
# min_version = '0.6c6'
# else:
# min_version = '0.6a9'
#
# try:
# use_setuptools(min_version=min_version)
# except TypeError:
# # locally installed ez_setup won't have min_version
# use_setuptools()
#
# from setuptools import setup, find_packages, Extension, Feature
VERSION = __version__
DESCRIPTION = 'SleekXMPP is an elegant Python library for XMPP (aka Jabber, Google Talk, etc).'
with codecs.open('README.rst', 'r', encoding='UTF-8') as readme:
LONG_DESCRIPTION = ''.join(readme)
CLASSIFIERS = [ 'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.1',
'Programming Language :: Python :: 3.2',
'Topic :: Software Development :: Libraries :: Python Modules',
]
packages = [ 'sleekxmpp',
'sleekxmpp/stanza',
'sleekxmpp/test',
'sleekxmpp/roster',
'sleekxmpp/xmlstream',
'sleekxmpp/xmlstream/matcher',
'sleekxmpp/xmlstream/handler',
'sleekxmpp/plugins',
'sleekxmpp/plugins/xep_0004',
'sleekxmpp/plugins/xep_0004/stanza',
'sleekxmpp/plugins/xep_0009',
'sleekxmpp/plugins/xep_0009/stanza',
'sleekxmpp/plugins/xep_0012',
'sleekxmpp/plugins/xep_0027',
'sleekxmpp/plugins/xep_0030',
'sleekxmpp/plugins/xep_0030/stanza',
'sleekxmpp/plugins/xep_0047',
'sleekxmpp/plugins/xep_0050',
'sleekxmpp/plugins/xep_0054',
'sleekxmpp/plugins/xep_0059',
'sleekxmpp/plugins/xep_0060',
'sleekxmpp/plugins/xep_0060/stanza',
'sleekxmpp/plugins/xep_0066',
'sleekxmpp/plugins/xep_0077',
'sleekxmpp/plugins/xep_0078',
'sleekxmpp/plugins/xep_0080',
'sleekxmpp/plugins/xep_0085',
'sleekxmpp/plugins/xep_0086',
'sleekxmpp/plugins/xep_0092',
'sleekxmpp/plugins/xep_0107',
'sleekxmpp/plugins/xep_0108',
'sleekxmpp/plugins/xep_0115',
'sleekxmpp/plugins/xep_0118',
'sleekxmpp/plugins/xep_0128',
'sleekxmpp/plugins/xep_0153',
'sleekxmpp/plugins/xep_0172',
'sleekxmpp/plugins/xep_0184',
'sleekxmpp/plugins/xep_0198',
'sleekxmpp/plugins/xep_0199',
'sleekxmpp/plugins/xep_0202',
'sleekxmpp/plugins/xep_0203',
'sleekxmpp/plugins/xep_0224',
'sleekxmpp/plugins/xep_0231',
'sleekxmpp/plugins/xep_0249',
'sleekxmpp/features',
'sleekxmpp/features/feature_mechanisms',
'sleekxmpp/features/feature_mechanisms/stanza',
'sleekxmpp/features/feature_starttls',
'sleekxmpp/features/feature_bind',
'sleekxmpp/features/feature_session',
'sleekxmpp/features/feature_rosterver',
'sleekxmpp/thirdparty',
'sleekxmpp/thirdparty/suelta',
'sleekxmpp/thirdparty/suelta/mechanisms',
]
setup(
name = "sleekxmpp",
version = VERSION,
description = DESCRIPTION,
long_description = LONG_DESCRIPTION,
author = 'Nathanael Fritz',
author_email = 'fritzy [at] netflint.net',
url = 'http://github.com/fritzy/SleekXMPP',
license = 'MIT',
platforms = [ 'any' ],
packages = packages,
requires = [ 'dnspython', 'pyasn1', 'pyasn1_modules' ],
classifiers = CLASSIFIERS,
cmdclass = {'test': TestCommand}
)

View File

@@ -1,245 +1,18 @@
#!/usr/bin/python2.5
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file license.txt for copying permission.
See the file LICENSE for copying permission.
"""
from __future__ import absolute_import, unicode_literals
from . basexmpp import basexmpp
from xml.etree import cElementTree as ET
from . xmlstream.xmlstream import XMLStream
from . xmlstream.xmlstream import RestartStream
from . xmlstream.matcher.xmlmask import MatchXMLMask
from . xmlstream.matcher.xpath import MatchXPath
from . xmlstream.matcher.many import MatchMany
from . xmlstream.handler.callback import Callback
from . xmlstream.stanzabase import StanzaBase
from . xmlstream import xmlstream as xmlstreammod
from . stanza.message import Message
from . stanza.iq import Iq
import time
import logging
import base64
import sys
import random
import copy
from . import plugins
#from . import stanza
srvsupport = True
try:
import dns.resolver
except ImportError:
srvsupport = False
from sleekxmpp.basexmpp import BaseXMPP
from sleekxmpp.clientxmpp import ClientXMPP
from sleekxmpp.componentxmpp import ComponentXMPP
from sleekxmpp.stanza import Message, Presence, Iq
from sleekxmpp.xmlstream.handler import *
from sleekxmpp.xmlstream import XMLStream, RestartStream
from sleekxmpp.xmlstream.matcher import *
from sleekxmpp.xmlstream.stanzabase import StanzaBase, ET
#class PresenceStanzaType(object):
#
# def fromXML(self, xml):
# self.ptype = xml.get('type')
class ClientXMPP(basexmpp, XMLStream):
"""SleekXMPP's client class. Use only for good, not evil."""
def __init__(self, jid, password, ssl=False, plugin_config = {}, plugin_whitelist=[], escape_quotes=True):
global srvsupport
XMLStream.__init__(self)
self.default_ns = 'jabber:client'
basexmpp.__init__(self)
self.plugin_config = plugin_config
self.escape_quotes = escape_quotes
self.set_jid(jid)
self.plugin_whitelist = plugin_whitelist
self.auto_reconnect = True
self.srvsupport = srvsupport
self.password = password
self.registered_features = []
self.stream_header = """<stream:stream to='%s' xmlns:stream='http://etherx.jabber.org/streams' xmlns='%s' version='1.0'>""" % (self.server,self.default_ns)
self.stream_footer = "</stream:stream>"
#self.map_namespace('http://etherx.jabber.org/streams', 'stream')
#self.map_namespace('jabber:client', '')
self.features = []
#TODO: Use stream state here
self.authenticated = False
self.sessionstarted = False
self.registerHandler(Callback('Stream Features', MatchXPath('{http://etherx.jabber.org/streams}features'), self._handleStreamFeatures, thread=True))
self.registerHandler(Callback('Roster Update', MatchXPath('{%s}iq/{jabber:iq:roster}query' % self.default_ns), self._handleRoster, thread=True))
#self.registerHandler(Callback('Roster Update', MatchXMLMask("<presence xmlns='%s' type='subscribe' />" % self.default_ns), self._handlePresenceSubscribe, thread=True))
self.registerFeature("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' />", self.handler_starttls, True)
self.registerFeature("<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />", self.handler_sasl_auth, True)
self.registerFeature("<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind' />", self.handler_bind_resource)
self.registerFeature("<session xmlns='urn:ietf:params:xml:ns:xmpp-session' />", self.handler_start_session)
#self.registerStanzaExtension('PresenceStanza', PresenceStanzaType)
#self.register_plugins()
def __getitem__(self, key):
if key in self.plugin:
return self.plugin[key]
else:
logging.warning("""Plugin "%s" is not loaded.""" % key)
return False
def get(self, key, default):
return self.plugin.get(key, default)
def connect(self, address=tuple()):
"""Connect to the Jabber Server. Attempts SRV lookup, and if it fails, uses
the JID server."""
if not address or len(address) < 2:
if not self.srvsupport:
logging.debug("Did not supply (address, port) to connect to and no SRV support is installed (http://www.dnspython.org). Continuing to attempt connection, using server hostname from JID.")
else:
logging.debug("Since no address is supplied, attempting SRV lookup.")
try:
answers = dns.resolver.query("_xmpp-client._tcp.%s" % self.server, "SRV")
except dns.resolver.NXDOMAIN:
logging.debug("No appropriate SRV record found. Using JID server name.")
else:
# pick a random answer, weighted by priority
# there are less verbose ways of doing this (random.choice() with answer * priority), but I chose this way anyway
# suggestions are welcome
addresses = {}
intmax = 0
priorities = []
for answer in answers:
intmax += answer.priority
addresses[intmax] = (answer.target.to_text()[:-1], answer.port)
priorities.append(intmax) # sure, I could just do priorities = addresses.keys()\n priorities.sort()
picked = random.randint(0, intmax)
for priority in priorities:
if picked <= priority:
address = addresses[priority]
break
if not address:
# if all else fails take server from JID.
address = (self.server, 5222)
result = XMLStream.connect(self, address[0], address[1], use_tls=True)
if result:
self.event("connected")
else:
logging.warning("Failed to connect")
self.event("disconnected")
return result
# overriding reconnect and disconnect so that we can get some events
# should events be part of or required by xmlstream? Maybe that would be cleaner
def reconnect(self):
logging.info("Reconnecting")
self.event("disconnected")
XMLStream.reconnect(self)
def disconnect(self, init=True, close=False, reconnect=False):
self.event("disconnected")
XMLStream.disconnect(self, reconnect)
def registerFeature(self, mask, pointer, breaker = False):
"""Register a stream feature."""
self.registered_features.append((MatchXMLMask(mask), pointer, breaker))
def updateRoster(self, jid, name=None, subscription=None, groups=[]):
"""Add or change a roster item."""
iq = self.Iq().setValues({'type': 'set'})
iq['roster'] = {jid: {'name': name, 'subscription': subscription, 'groups': groups}}
#self.send(iq, self.Iq().setValues({'id': iq['id']}))
r = iq.send()
return r['type'] == 'result'
def getRoster(self):
"""Request the roster be sent."""
iq = self.Iq().setValues({'type': 'get'}).enable('roster').send()
self._handleRoster(iq, request=True)
def _handleStreamFeatures(self, features):
self.features = []
for sub in features.xml:
self.features.append(sub.tag)
for subelement in features.xml:
for feature in self.registered_features:
if feature[0].match(subelement):
#if self.maskcmp(subelement, feature[0], True):
if feature[1](subelement) and feature[2]: #if breaker, don't continue
return True
def handler_starttls(self, xml):
if not self.authenticated and self.ssl_support:
self.add_handler("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls' />", self.handler_tls_start, instream=True)
self.sendXML(xml)
return True
else:
logging.warning("The module tlslite is required in to some servers, and has not been found.")
return False
def handler_tls_start(self, xml):
logging.debug("Starting TLS")
if self.startTLS():
raise RestartStream()
def handler_sasl_auth(self, xml):
if '{urn:ietf:params:xml:ns:xmpp-tls}starttls' in self.features:
return False
logging.debug("Starting SASL Auth")
self.add_handler("<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />", self.handler_auth_success, instream=True)
self.add_handler("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl' />", self.handler_auth_fail, instream=True)
sasl_mechs = xml.findall('{urn:ietf:params:xml:ns:xmpp-sasl}mechanism')
if len(sasl_mechs):
for sasl_mech in sasl_mechs:
self.features.append("sasl:%s" % sasl_mech.text)
if 'sasl:PLAIN' in self.features:
if sys.version_info < (3,0):
self.send("""<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>%s</auth>""" % base64.b64encode(b'\x00' + bytes(self.username) + b'\x00' + bytes(self.password)).decode('utf-8'))
else:
self.send("""<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>%s</auth>""" % base64.b64encode(b'\x00' + bytes(self.username, 'utf-8') + b'\x00' + bytes(self.password, 'utf-8')).decode('utf-8'))
else:
logging.error("No appropriate login method.")
self.disconnect()
#if 'sasl:DIGEST-MD5' in self.features:
# self._auth_digestmd5()
return True
def handler_auth_success(self, xml):
self.authenticated = True
self.features = []
raise RestartStream()
def handler_auth_fail(self, xml):
logging.info("Authentication failed.")
self.disconnect()
self.event("failed_auth")
def handler_bind_resource(self, xml):
logging.debug("Requesting resource: %s" % self.resource)
iq = self.Iq(stype='set')
res = ET.Element('resource')
res.text = self.resource
xml.append(res)
iq.append(xml)
response = iq.send()
#response = self.send(iq, self.Iq(sid=iq['id']))
self.set_jid(response.xml.find('{urn:ietf:params:xml:ns:xmpp-bind}bind/{urn:ietf:params:xml:ns:xmpp-bind}jid').text)
logging.info("Node set to: %s" % self.fulljid)
if "{urn:ietf:params:xml:ns:xmpp-session}session" not in self.features:
logging.debug("Established Session")
self.sessionstarted = True
self.event("session_start")
def handler_start_session(self, xml):
if self.authenticated:
response = self.send(self.makeIqSet(xml), self.makeIq(self.getId()))
logging.debug("Established Session")
self.sessionstarted = True
self.event("session_start")
def _handleRoster(self, iq, request=False):
if iq['type'] == 'set' or (iq['type'] == 'result' and request):
for jid in iq['roster']['items']:
if not jid in self.roster:
self.roster[jid] = {'groups': [], 'name': '', 'subscription': 'none', 'presence': {}, 'in_roster': True}
self.roster[jid].update(iq['roster']['items'][jid])
if iq['type'] == 'set':
self.send(self.Iq().setValues({'type': 'result', 'id': iq['id']}).enable('roster'))
self.event("roster_update", iq)
from sleekxmpp.version import __version__, __version_info__

198
sleekxmpp/api.py Normal file
View File

@@ -0,0 +1,198 @@
from sleekxmpp.xmlstream import JID
class APIWrapper(object):
def __init__(self, api, name):
self.api = api
self.name = name
if name not in self.api.settings:
self.api.settings[name] = {}
def __getattr__(self, attr):
"""Curry API management commands with the API name."""
if attr == 'name':
return self.name
elif attr == 'settings':
return self.api.settings[self.name]
elif attr == 'register':
def curried_handler(handler, op, jid=None, node=None, default=False):
register = getattr(self.api, attr)
return register(handler, self.name, op, jid, node, default)
return curried_handler
elif attr == 'register_default':
def curried_handler(handler, op, jid=None, node=None):
return getattr(self.api, attr)(handler, self.name, op)
return curried_handler
elif attr in ('run', 'restore_default', 'unregister'):
def curried_handler(*args, **kwargs):
return getattr(self.api, attr)(self.name, *args, **kwargs)
return curried_handler
return None
def __getitem__(self, attr):
def curried_handler(jid=None, node=None, ifrom=None, args=None):
return self.api.run(self.name, attr, jid, node, ifrom, args)
return curried_handler
class APIRegistry(object):
def __init__(self, xmpp):
self._handlers = {}
self._handler_defaults = {}
self.xmpp = xmpp
self.settings = {}
def _setup(self, ctype, op):
"""Initialize the API callback dictionaries.
:param string ctype: The name of the API to initialize.
:param string op: The API operation to initialize.
"""
if ctype not in self.settings:
self.settings[ctype] = {}
if ctype not in self._handler_defaults:
self._handler_defaults[ctype] = {}
if ctype not in self._handlers:
self._handlers[ctype] = {}
if op not in self._handlers[ctype]:
self._handlers[ctype][op] = {'global': None,
'jid': {},
'node': {}}
def wrap(self, ctype):
"""Return a wrapper object that targets a specific API."""
return APIWrapper(self, ctype)
def purge(self, ctype):
"""Remove all information for a given API."""
del self.settings[ctype]
del self._handler_defaults[ctype]
del self._handlers[ctype]
def run(self, ctype, op, jid=None, node=None, ifrom=None, args=None):
"""Execute an API callback, based on specificity.
The API callback that is executed is chosen based on the combination
of the provided JID and node:
JID | node | Handler
==============================
Given | Given | Node handler
Given | None | JID handler
None | None | Global handler
A node handler is responsible for servicing a single node at a single
JID, while a JID handler may respond for any node at a given JID, and
the global handler will answer to any JID+node combination.
Handlers should check that the JID ``ifrom`` is authorized to perform
the desired action.
:param string ctype: The name of the API to use.
:param string op: The API operation to perform.
:param JID jid: Optionally provide specific JID.
:param string node: Optionally provide specific node.
:param JID ifrom: Optionally provide the requesting JID.
:param tuple args: Optional positional arguments to the handler.
"""
self._setup(ctype, op)
if jid in (None, ''):
jid = self.xmpp.boundjid
if jid and not isinstance(jid, JID):
jid = JID(jid)
if node is None:
node = ''
if self.xmpp.is_component:
if self.settings[ctype].get('component_bare', False):
jid = jid.bare
else:
jid = jid.full
else:
if self.settings[ctype].get('client_bare', True):
jid = jid.bare
else:
jid = jid.full
jid = JID(jid)
handler = self._handlers[ctype][op]['node'].get((jid, node), None)
if handler is None:
handler = self._handlers[ctype][op]['jid'].get(jid, None)
if handler is None:
handler = self._handlers[ctype][op].get('global', None)
if handler:
try:
return handler(jid, node, ifrom, args)
except TypeError:
# To preserve backward compatibility, drop the ifrom
# parameter for existing handlers that don't understand it.
return handler(jid, node, args)
def register(self, handler, ctype, op, jid=None, node=None, default=False):
"""Register an API callback, with JID+node specificity.
The API callback can later be executed based on the
specificity of the provided JID+node combination.
See :meth:`~ApiRegistry.run` for more details.
:param string ctype: The name of the API to use.
:param string op: The API operation to perform.
:param JID jid: Optionally provide specific JID.
:param string node: Optionally provide specific node.
"""
self._setup(ctype, op)
if jid is None and node is None:
if handler is None:
handler = self._handler_defaults[op]
self._handlers[ctype][op]['global'] = handler
elif jid is not None and node is None:
self._handlers[ctype][op]['jid'][jid] = handler
else:
self._handlers[ctype][op]['node'][(jid, node)] = handler
if default:
self.register_default(handler, ctype, op)
def register_default(self, handler, ctype, op):
"""Register a default, global handler for an operation.
:param func handler: The default, global handler for the operation.
:param string ctype: The name of the API to modify.
:param string op: The API operation to use.
"""
self._setup(ctype, op)
self._handler_defaults[ctype][op] = handler
def unregister(self, ctype, op, jid=None, node=None):
"""Remove an API callback.
The API callback chosen for removal is based on the
specificity of the provided JID+node combination.
See :meth:`~ApiRegistry.run` for more details.
:param string ctype: The name of the API to use.
:param string op: The API operation to perform.
:param JID jid: Optionally provide specific JID.
:param string node: Optionally provide specific node.
"""
self._setup(ctype, op)
self.register(None, ctype, op, jid, node)
def restore_default(self, ctype, op, jid=None, node=None):
"""Reset an API callback to use a default handler.
:param string ctype: The name of the API to use.
:param string op: The API operation to perform.
:param JID jid: Optionally provide specific JID.
:param string node: Optionally provide specific node.
"""
self.unregister(ctype, op, jid, node)
self.register(self._handler_defaults[ctype][op], ctype, op, jid, node)

File diff suppressed because it is too large Load Diff

302
sleekxmpp/clientxmpp.py Normal file
View File

@@ -0,0 +1,302 @@
# -*- coding: utf-8 -*-
"""
sleekxmpp.clientxmpp
~~~~~~~~~~~~~~~~~~~~
This module provides XMPP functionality that
is specific to client connections.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
from __future__ import absolute_import, unicode_literals
import logging
from sleekxmpp.stanza import StreamFeatures
from sleekxmpp.basexmpp import BaseXMPP
from sleekxmpp.exceptions import XMPPError
from sleekxmpp.xmlstream import XMLStream
from sleekxmpp.xmlstream.matcher import StanzaPath, MatchXPath
from sleekxmpp.xmlstream.handler import Callback
# Flag indicating if DNS SRV records are available for use.
try:
import dns.resolver
except ImportError:
DNSPYTHON = False
else:
DNSPYTHON = True
log = logging.getLogger(__name__)
class ClientXMPP(BaseXMPP):
"""
SleekXMPP's client class. (Use only for good, not for evil.)
Typical use pattern:
.. code-block:: python
xmpp = ClientXMPP('user@server.tld/resource', 'password')
# ... Register plugins and event handlers ...
xmpp.connect()
xmpp.process(block=False) # block=True will block the current
# thread. By default, block=False
:param jid: The JID of 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_whitelist: A list of approved plugins that
will be loaded when calling
:meth:`~sleekxmpp.basexmpp.BaseXMPP.register_plugins()`.
:param escape_quotes: **Deprecated.**
"""
def __init__(self, jid, password, ssl=False, plugin_config={},
plugin_whitelist=[], escape_quotes=True, sasl_mech=None):
BaseXMPP.__init__(self, jid, 'jabber:client')
self.set_jid(jid)
self.escape_quotes = escape_quotes
self.plugin_config = plugin_config
self.plugin_whitelist = plugin_whitelist
self.default_port = 5222
self.credentials = {}
self.password = password
self.stream_header = "<stream:stream to='%s' %s %s version='1.0'>" % (
self.boundjid.host,
"xmlns:stream='%s'" % self.stream_ns,
"xmlns='%s'" % self.default_ns)
self.stream_footer = "</stream:stream>"
self.features = set()
self._stream_feature_handlers = {}
self._stream_feature_order = []
self.dns_service = 'xmpp-client'
#TODO: Use stream state here
self.authenticated = False
self.sessionstarted = False
self.bound = False
self.bindfail = False
self.add_event_handler('connected', self._handle_connected)
self.add_event_handler('session_bind', self._handle_session_bind)
self.register_stanza(StreamFeatures)
self.register_handler(
Callback('Stream Features',
MatchXPath('{%s}features' % self.stream_ns),
self._handle_stream_features))
self.register_handler(
Callback('Roster Update',
StanzaPath('iq@type=set/roster'),
self._handle_roster))
# Setup default stream features
self.register_plugin('feature_starttls')
self.register_plugin('feature_bind')
self.register_plugin('feature_session')
self.register_plugin('feature_mechanisms',
pconfig={'use_mech': sasl_mech} if sasl_mech else None)
self.register_plugin('feature_rosterver')
@property
def password(self):
return self.credentials.get('password', '')
@password.setter
def password(self, value):
self.credentials['password'] = value
def connect(self, address=tuple(), reattempt=True,
use_tls=True, use_ssl=False):
"""Connect to the XMPP server.
When no address is given, a SRV lookup for the server will
be attempted. If that fails, the server user in the JID
will be used.
:param address -- A tuple containing the server's host and port.
:param reattempt: If ``True``, repeat attempting to connect if an
error occurs. Defaults to ``True``.
:param use_tls: Indicates if TLS should be used for the
connection. Defaults to ``True``.
:param use_ssl: Indicates if the older SSL connection method
should be used. Defaults to ``False``.
"""
self.session_started_event.clear()
# If an address was provided, disable using DNS SRV lookup;
# otherwise, use the domain from the client JID with the standard
# XMPP client port and allow SRV lookup.
if address:
self.dns_service = None
else:
address = (self.boundjid.host, 5222)
self.dns_service = 'xmpp-client'
self._expected_server_name = self.boundjid.host
return XMLStream.connect(self, address[0], address[1],
use_tls=use_tls, use_ssl=use_ssl,
reattempt=reattempt)
def register_feature(self, name, handler, restart=False, order=5000):
"""Register a stream feature handler.
:param name: The name of the stream feature.
:param handler: The function to execute if the feature is received.
:param restart: Indicates if feature processing should halt with
this feature. Defaults to ``False``.
:param order: The relative ordering in which the feature should
be negotiated. Lower values will be attempted
earlier when available.
"""
self._stream_feature_handlers[name] = (handler, restart)
self._stream_feature_order.append((order, name))
self._stream_feature_order.sort()
def update_roster(self, jid, name=None, subscription=None, groups=[],
block=True, timeout=None, callback=None):
"""Add or change a roster item.
:param jid: The JID of the entry to modify.
:param name: The user's nickname for this JID.
:param subscription: The subscription status. May be one of
``'to'``, ``'from'``, ``'both'``, or
``'none'``. If set to ``'remove'``,
the entry will be deleted.
:param groups: The roster groups that contain this item.
:param block: Specify if the roster request will block
until a response is received, or a timeout
occurs. Defaults to ``True``.
:param timeout: The length of time (in seconds) to wait
for a response before continuing if blocking
is used. Defaults to
:attr:`~sleekxmpp.xmlstream.xmlstream.XMLStream.response_timeout`.
:param callback: Optional reference to a stream handler function.
Will be executed when the roster is received.
Implies ``block=False``.
"""
return self.client_roster.update(jid, name, subscription, groups,
block, timeout, callback)
def del_roster_item(self, jid):
"""Remove an item from the roster.
This is done by setting its subscription status to ``'remove'``.
:param jid: The JID of the item to remove.
"""
return self.client_roster.remove(jid)
def get_roster(self, block=True, timeout=None, callback=None):
"""Request the roster from the server.
:param block: Specify if the roster request will block until a
response is received, or a timeout occurs.
Defaults to ``True``.
:param timeout: The length of time (in seconds) to wait for a response
before continuing if blocking is used.
Defaults to
:attr:`~sleekxmpp.xmlstream.xmlstream.XMLStream.response_timeout`.
:param callback: Optional reference to a stream handler function. Will
be executed when the roster is received.
Implies ``block=False``.
"""
iq = self.Iq()
iq['type'] = 'get'
iq.enable('roster')
if 'rosterver' in self.features:
iq['roster']['ver'] = self.client_roster.version
if not block and callback is None:
callback = lambda resp: self._handle_roster(resp)
response = iq.send(block, timeout, callback)
self.event('roster_received', response)
if block:
self._handle_roster(response)
return response
def _handle_connected(self, event=None):
#TODO: Use stream state here
self.authenticated = False
self.sessionstarted = False
self.bound = False
self.bindfail = False
self.features = set()
def _handle_stream_features(self, features):
"""Process the received stream features.
:param features: The features stanza.
"""
for order, name in self._stream_feature_order:
if name in features['features']:
handler, restart = self._stream_feature_handlers[name]
if handler(features) and restart:
# Don't continue if the feature requires
# restarting the XML stream.
return True
def _handle_roster(self, iq):
"""Update the roster after receiving a roster stanza.
:param iq: The roster stanza.
"""
if iq['type'] == 'set':
if iq['from'].bare and iq['from'].bare != self.boundjid.bare:
raise XMPPError(condition='service-unavailable')
roster = self.client_roster
if iq['roster']['ver']:
roster.version = iq['roster']['ver']
for jid in iq['roster']['items']:
item = iq['roster']['items'][jid]
roster[jid]['name'] = item['name']
roster[jid]['groups'] = item['groups']
roster[jid]['from'] = item['subscription'] in ['from', 'both']
roster[jid]['to'] = item['subscription'] in ['to', 'both']
roster[jid]['pending_out'] = (item['ask'] == 'subscribe')
roster[jid].save(remove=(item['subscription'] == 'remove'))
self.event("roster_update", iq)
if iq['type'] == 'set':
resp = self.Iq(stype='result',
sto=iq['from'],
sid=iq['id'])
resp.enable('roster')
resp.send()
def _handle_session_bind(self, jid):
"""Set the client roster to the JID set by the server.
:param :class:`sleekxmpp.xmlstream.jid.JID` jid: The bound JID as
dictated by the server. The same as :attr:`boundjid`.
"""
self.client_roster = self.roster[jid]
# To comply with PEP8, method names now use underscores.
# Deprecated method names are re-mapped for backwards compatibility.
ClientXMPP.updateRoster = ClientXMPP.update_roster
ClientXMPP.delRosterItem = ClientXMPP.del_roster_item
ClientXMPP.getRoster = ClientXMPP.get_roster
ClientXMPP.registerFeature = ClientXMPP.register_feature

View File

@@ -1,41 +0,0 @@
import sleekxmpp.componentxmpp
import logging
from optparse import OptionParser
import time
class Example(sleekxmpp.componentxmpp.ComponentXMPP):
def __init__(self, jid, password):
sleekxmpp.componentxmpp.ComponentXMPP.__init__(self, jid, password, 'vm1', 5230)
self.add_event_handler("session_start", self.start)
self.add_event_handler("message", self.message)
def start(self, event):
#self.getRoster()
#self.sendPresence(pto='admin@tigase.netflint.net/sarkozy')
#self.sendPresence(pto='tigase.netflint.net')
pass
def message(self, event):
self.sendMessage("%s/%s" % (event['jid'], event['resource']), "Thanks for sending me, \"%s\"." % event['message'], mtype=event['type'])
if __name__ == '__main__':
#parse command line arguements
optp = OptionParser()
optp.add_option('-q','--quiet', help='set logging to ERROR', action='store_const', dest='loglevel', const=logging.ERROR, default=logging.INFO)
optp.add_option('-d','--debug', help='set logging to DEBUG', action='store_const', dest='loglevel', const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v','--verbose', help='set logging to COMM', action='store_const', dest='loglevel', const=5, default=logging.INFO)
optp.add_option("-c","--config", dest="configfile", default="config.xml", help="set config file to use")
opts,args = optp.parse_args()
logging.basicConfig(level=opts.loglevel, format='%(levelname)-8s %(message)s')
xmpp = Example('component.vm1', 'secreteating')
xmpp.registerPlugin('xep_0004')
xmpp.registerPlugin('xep_0030')
xmpp.registerPlugin('xep_0060')
xmpp.registerPlugin('xep_0199')
if xmpp.connect():
xmpp.process(threaded=False)
print("done")
else:
print("Unable to connect.")

219
sleekxmpp/componentxmpp.py Executable file → Normal file
View File

@@ -1,78 +1,165 @@
#!/usr/bin/python2.5
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.clientxmpp
~~~~~~~~~~~~~~~~~~~~
See the file license.txt for copying permission.
This module provides XMPP functionality that
is specific to external server component connections.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
from __future__ import absolute_import
from . basexmpp import basexmpp
from xml.etree import cElementTree as ET
from . xmlstream.xmlstream import XMLStream
from . xmlstream.xmlstream import RestartStream
from . xmlstream.matcher.xmlmask import MatchXMLMask
from . xmlstream.matcher.xpath import MatchXPath
from . xmlstream.matcher.many import MatchMany
from . xmlstream.handler.callback import Callback
from . xmlstream.stanzabase import StanzaBase
from . xmlstream import xmlstream as xmlstreammod
import time
import logging
import base64
import sys
import random
import copy
from . import plugins
from . import stanza
import hashlib
srvsupport = True
try:
import dns.resolver
except ImportError:
srvsupport = False
from sleekxmpp.basexmpp import BaseXMPP
from sleekxmpp.xmlstream import XMLStream
from sleekxmpp.xmlstream import ET
from sleekxmpp.xmlstream.matcher import MatchXPath
from sleekxmpp.xmlstream.handler import Callback
class ComponentXMPP(basexmpp, XMLStream):
"""SleekXMPP's client class. Use only for good, not evil."""
log = logging.getLogger(__name__)
def __init__(self, jid, secret, host, port, plugin_config = {}, plugin_whitelist=[], use_jc_ns=False):
XMLStream.__init__(self)
if use_jc_ns:
self.default_ns = 'jabber:client'
else:
self.default_ns = 'jabber:component:accept'
basexmpp.__init__(self)
self.auto_authorize = None
self.stream_header = "<stream:stream xmlns='jabber:component:accept' xmlns:stream='http://etherx.jabber.org/streams' to='%s'>" % jid
self.stream_footer = "</stream:stream>"
self.server_host = host
self.server_port = port
self.set_jid(jid)
self.secret = secret
self.registerHandler(Callback('Handshake', MatchXPath('{jabber:component:accept}handshake'), self._handleHandshake))
def incoming_filter(self, xmlobj):
if xmlobj.tag.startswith('{jabber:client}'):
xmlobj.tag = xmlobj.tag.replace('jabber:client', self.default_ns)
for sub in xmlobj:
self.incoming_filter(sub)
return xmlobj
def start_stream_handler(self, xml):
sid = xml.get('id', '')
handshake = ET.Element('{jabber:component:accept}handshake')
if sys.version_info < (3,0):
handshake.text = hashlib.sha1("%s%s" % (sid, self.secret)).hexdigest().lower()
else:
handshake.text = hashlib.sha1(bytes("%s%s" % (sid, self.secret), 'utf-8')).hexdigest().lower()
self.sendXML(handshake)
def _handleHandshake(self, xml):
self.event("session_start")
def connect(self):
logging.debug("Connecting to %s:%s" % (self.server_host, self.server_port))
return xmlstreammod.XMLStream.connect(self, self.server_host, self.server_port)
class ComponentXMPP(BaseXMPP):
"""
SleekXMPP's basic XMPP server component.
Use only for good, not for evil.
:param jid: The JID of the component.
:param secret: The secret or password for the component.
:param host: The server accepting the component.
:param port: The port used to connect to the server.
:param plugin_config: A dictionary of plugin configurations.
:param plugin_whitelist: A list of approved plugins that
will be loaded when calling
:meth:`~sleekxmpp.basexmpp.BaseXMPP.register_plugins()`.
:param use_jc_ns: Indicates if the ``'jabber:client'`` namespace
should be used instead of the standard
``'jabber:component:accept'`` namespace.
Defaults to ``False``.
"""
def __init__(self, jid, secret, host=None, port=None,
plugin_config={}, plugin_whitelist=[], use_jc_ns=False):
if use_jc_ns:
default_ns = 'jabber:client'
else:
default_ns = 'jabber:component:accept'
BaseXMPP.__init__(self, jid, default_ns)
self.auto_authorize = None
self.stream_header = "<stream:stream %s %s to='%s'>" % (
'xmlns="jabber:component:accept"',
'xmlns:stream="%s"' % self.stream_ns,
jid)
self.stream_footer = "</stream:stream>"
self.server_host = host
self.server_port = port
self.secret = secret
self.plugin_config = plugin_config
self.plugin_whitelist = plugin_whitelist
self.is_component = True
self.register_handler(
Callback('Handshake',
MatchXPath('{jabber:component:accept}handshake'),
self._handle_handshake))
self.add_event_handler('presence_probe',
self._handle_probe)
def connect(self, host=None, port=None, use_ssl=False,
use_tls=False, reattempt=True):
"""Connect to the server.
Setting ``reattempt`` to ``True`` will cause connection attempts to
be made every second until a successful connection is established.
:param host: The name of the desired server for the connection.
Defaults to :attr:`server_host`.
:param port: Port to connect to on the server.
Defauts to :attr:`server_port`.
:param use_ssl: Flag indicating if SSL should be used by connecting
directly to a port using SSL.
:param use_tls: Flag indicating if TLS should be used, allowing for
connecting to a port without using SSL immediately and
later upgrading the connection.
:param reattempt: Flag indicating if the socket should reconnect
after disconnections.
"""
if host is None:
host = self.server_host
if port is None:
port = self.server_port
self.server_name = self.boundjid.host
if use_tls:
log.info("XEP-0114 components can not use TLS")
log.debug("Connecting to %s:%s", host, port)
return XMLStream.connect(self, host=host, port=port,
use_ssl=use_ssl,
use_tls=False,
reattempt=reattempt)
def incoming_filter(self, xml):
"""
Pre-process incoming XML stanzas by converting any
``'jabber:client'`` namespaced elements to the component's
default namespace.
:param xml: The XML stanza to pre-process.
"""
if xml.tag.startswith('{jabber:client}'):
xml.tag = xml.tag.replace('jabber:client', self.default_ns)
# The incoming_filter call is only made on top level stanza
# elements. So we manually continue filtering on sub-elements.
for sub in xml:
self.incoming_filter(sub)
return xml
def start_stream_handler(self, xml):
"""
Once the streams are established, attempt to handshake
with the server to be accepted as a component.
:param xml: The incoming stream's root element.
"""
BaseXMPP.start_stream_handler(self, xml)
# Construct a hash of the stream ID and the component secret.
sid = xml.get('id', '')
pre_hash = '%s%s' % (sid, self.secret)
if sys.version_info >= (3, 0):
# Handle Unicode byte encoding in Python 3.
pre_hash = bytes(pre_hash, 'utf-8')
handshake = ET.Element('{jabber:component:accept}handshake')
handshake.text = hashlib.sha1(pre_hash).hexdigest().lower()
self.send_xml(handshake, now=True)
def _handle_handshake(self, xml):
"""The handshake has been accepted.
:param xml: The reply handshake stanza.
"""
self.session_started_event.set()
self.event("session_start")
def _handle_probe(self, presence):
pto = presence['to'].bare
pfrom = presence['from'].bare
self.roster[pto][pfrom].handle_probe(presence)

View File

@@ -1,16 +1,90 @@
# -*- coding: utf-8 -*-
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
sleekxmpp.exceptions
~~~~~~~~~~~~~~~~~~~~
See the file license.txt for copying permission.
Part of SleekXMPP: The Sleek XMPP Library
:copyright: (c) 2011 Nathanael C. Fritz
:license: MIT, see LICENSE for more details
"""
class XMPPError(Exception):
def __init__(self, condition='undefined-condition', text=None, etype=None, extension=None, extension_ns=None, extension_args=None):
self.condition = condition
self.text = text
self.etype = etype
self.extension = extension
self.extension_ns = extension_ns
self.extension_args = extension_args
"""
A generic exception that may be raised while processing an XMPP stanza
to indicate that an error response stanza should be sent.
The exception method for stanza objects extending
:class:`~sleekxmpp.stanza.rootstanza.RootStanza` will create an error
stanza and initialize any additional substanzas using the extension
information included in the exception.
Meant for use in SleekXMPP plugins and applications using SleekXMPP.
Extension information can be included to add additional XML elements
to the generated error stanza.
:param condition: The XMPP defined error condition.
Defaults to ``'undefined-condition'``.
:param text: Human readable text describing the error.
:param etype: The XMPP error type, such as ``'cancel'`` or ``'modify'``.
Defaults to ``'cancel'``.
:param extension: Tag name of the extension's XML content.
:param extension_ns: XML namespace of the extensions' XML content.
:param extension_args: Content and attributes for the extension
element. Same as the additional arguments to
the :class:`~xml.etree.ElementTree.Element`
constructor.
:param clear: Indicates if the stanza's contents should be
removed before replying with an error.
Defaults to ``True``.
"""
def __init__(self, condition='undefined-condition', text=None,
etype='cancel', extension=None, extension_ns=None,
extension_args=None, clear=True):
if extension_args is None:
extension_args = {}
self.condition = condition
self.text = text
self.etype = etype
self.clear = clear
self.extension = extension
self.extension_ns = extension_ns
self.extension_args = extension_args
class IqTimeout(XMPPError):
"""
An exception which indicates that an IQ request response has not been
received within the alloted time window.
"""
def __init__(self, iq):
super(IqTimeout, self).__init__(
condition='remote-server-timeout',
etype='cancel')
#: The :class:`~sleekxmpp.stanza.iq.Iq` stanza whose response
#: did not arrive before the timeout expired.
self.iq = iq
class IqError(XMPPError):
"""
An exception raised when an Iq stanza of type 'error' is received
after making a blocking send call.
"""
def __init__(self, iq):
super(IqError, self).__init__(
condition=iq['error']['condition'],
text=iq['error']['text'],
etype=iq['error']['type'])
#: The :class:`~sleekxmpp.stanza.iq.Iq` error result stanza.
self.iq = iq

View File

@@ -0,0 +1,15 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
__all__ = [
'feature_starttls',
'feature_mechanisms',
'feature_bind',
'feature_session',
'feature_rosterver'
]

View File

@@ -0,0 +1,19 @@
"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2011 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
from sleekxmpp.plugins.base import register_plugin
from sleekxmpp.features.feature_bind.bind import FeatureBind
from sleekxmpp.features.feature_bind.stanza import Bind
register_plugin(FeatureBind)
# Retain some backwards compatibility
feature_bind = FeatureBind

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