Compare commits
66 Commits
1.1.3
...
sleek-1.1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acd9c32a9f | ||
|
|
b8581b0278 | ||
|
|
917faecdcb | ||
|
|
f6edaa56a6 | ||
|
|
51fee28bf4 | ||
|
|
e8a3e92ceb | ||
|
|
5df3839b7a | ||
|
|
8dcb441f44 | ||
|
|
a347cf625a | ||
|
|
46f49c7a12 | ||
|
|
99701c947e | ||
|
|
1baae1b81e | ||
|
|
7d20f0e9a6 | ||
|
|
fbad22a1cd | ||
|
|
5af2f62c04 | ||
|
|
4a4a03858e | ||
|
|
6819b57353 | ||
|
|
88b5e60807 | ||
|
|
a26a8bd79c | ||
|
|
9307a6915f | ||
|
|
85ef2d8d0b | ||
|
|
c2c7cc032b | ||
|
|
e4911e9391 | ||
|
|
b11e1ee92d | ||
|
|
5027d00c10 | ||
|
|
69ddeceb49 | ||
|
|
82698672bb | ||
|
|
9cec284947 | ||
|
|
dc501d1902 | ||
|
|
100e504b7f | ||
|
|
8a745c5e81 | ||
|
|
bf0a157c5d | ||
|
|
f49818be06 | ||
|
|
1ad171dfe5 | ||
|
|
2a78570d65 | ||
|
|
7a112f2523 | ||
|
|
e86444e5fb | ||
|
|
36c11ad9de | ||
|
|
019a4b20ae | ||
|
|
433ee08687 | ||
|
|
7858d969d8 | ||
|
|
8119551049 | ||
|
|
061489f03a | ||
|
|
d92aa05b5c | ||
|
|
f7a74d960e | ||
|
|
95a0e51b41 | ||
|
|
110e45e187 | ||
|
|
534aaf2b2a | ||
|
|
4cc20fdd05 | ||
|
|
f3fae192a8 | ||
|
|
7d59a8a0ad | ||
|
|
8da387a38a | ||
|
|
ff6fc44215 | ||
|
|
62391a895a | ||
|
|
9bcdd7d18f | ||
|
|
5c4f7bfe8b | ||
|
|
0b7f134021 | ||
|
|
378a42889f | ||
|
|
f824950552 | ||
|
|
3d2d11f169 | ||
|
|
181aea737d | ||
|
|
ee702f4071 | ||
|
|
a08c2161a7 | ||
|
|
0e36a01354 | ||
|
|
c39ad7dfbb | ||
|
|
b92ae706e9 |
@@ -45,7 +45,7 @@ The latest source code for SleekXMPP may be found on `Github
|
|||||||
``develop`` branch.
|
``develop`` branch.
|
||||||
|
|
||||||
**Latest Release**
|
**Latest Release**
|
||||||
- `1.1.3 <http://github.com/fritzy/SleekXMPP/zipball/1.1.3>`_
|
- `1.1.9 <http://github.com/fritzy/SleekXMPP/zipball/1.1.9>`_
|
||||||
|
|
||||||
**Develop Releases**
|
**Develop Releases**
|
||||||
- `Latest Develop Version <http://github.com/fritzy/SleekXMPP/zipball/develop>`_
|
- `Latest Develop Version <http://github.com/fritzy/SleekXMPP/zipball/develop>`_
|
||||||
@@ -74,6 +74,7 @@ help with SleekXMPP.
|
|||||||
**Chat**
|
**Chat**
|
||||||
`sleek@conference.jabber.org <xmpp:sleek@conference.jabber.org?join>`_
|
`sleek@conference.jabber.org <xmpp:sleek@conference.jabber.org?join>`_
|
||||||
|
|
||||||
|
|
||||||
Documentation and Testing
|
Documentation and Testing
|
||||||
-------------------------
|
-------------------------
|
||||||
Documentation can be found both inline in the code, and as a Sphinx project in ``/docs``.
|
Documentation can be found both inline in the code, and as a Sphinx project in ``/docs``.
|
||||||
|
|||||||
@@ -122,6 +122,19 @@ if __name__ == '__main__':
|
|||||||
xmpp.register_plugin('xep_0060') # PubSub
|
xmpp.register_plugin('xep_0060') # PubSub
|
||||||
xmpp.register_plugin('xep_0199') # XMPP Ping
|
xmpp.register_plugin('xep_0199') # XMPP Ping
|
||||||
|
|
||||||
|
# If you are connecting to Facebook and wish to use the
|
||||||
|
# X-FACEBOOK-PLATFORM authentication mechanism, you will need
|
||||||
|
# your API key and an access token. Then you'll set:
|
||||||
|
# xmpp.credentials['api_key'] = 'THE_API_KEY'
|
||||||
|
# xmpp.credentials['access_token'] = 'THE_ACCESS_TOKEN'
|
||||||
|
|
||||||
|
# If you are connecting to MSN, then you will need an
|
||||||
|
# access token, and it does not matter what JID you
|
||||||
|
# specify other than that the domain is 'messenger.live.com',
|
||||||
|
# so '_@messenger.live.com' will work. You can specify
|
||||||
|
# the access token as so:
|
||||||
|
# xmpp.credentials['access_token'] = 'THE_ACCESS_TOKEN'
|
||||||
|
|
||||||
# If you are working with an OpenFire server, you may need
|
# If you are working with an OpenFire server, you may need
|
||||||
# to adjust the SSL version used:
|
# to adjust the SSL version used:
|
||||||
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
|
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
|
||||||
|
|||||||
165
examples/gtalk_custom_domain.py
Executable file
165
examples/gtalk_custom_domain.py
Executable file
@@ -0,0 +1,165 @@
|
|||||||
|
#!/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
|
||||||
|
|
||||||
|
import ssl
|
||||||
|
from sleekxmpp.xmlstream import cert
|
||||||
|
|
||||||
|
|
||||||
|
# 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 GTalkBot(sleekxmpp.ClientXMPP):
|
||||||
|
|
||||||
|
"""
|
||||||
|
A demonstration of using SleekXMPP with accounts from a Google Apps
|
||||||
|
account with a custom domain, because it requires custom certificate
|
||||||
|
validation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
# Using a Google Apps custom domain, the certificate
|
||||||
|
# does not contain the custom domain, just the GTalk
|
||||||
|
# server name. So we will need to process invalid
|
||||||
|
# certifcates ourselves and check that it really
|
||||||
|
# is from Google.
|
||||||
|
self.add_event_handler("ssl_invalid_cert", self.invalid_cert)
|
||||||
|
|
||||||
|
def invalid_cert(self, pem_cert):
|
||||||
|
der_cert = ssl.PEM_cert_to_DER_cert(pem_cert)
|
||||||
|
try:
|
||||||
|
cert.verify('talk.google.com', der_cert)
|
||||||
|
logging.debug("CERT: Found GTalk certificate")
|
||||||
|
except cert.CertificateError as err:
|
||||||
|
log.error(err.message)
|
||||||
|
self.disconnect(send_close=False)
|
||||||
|
|
||||||
|
def start(self, event):
|
||||||
|
"""
|
||||||
|
Process the session_start event.
|
||||||
|
|
||||||
|
Typical actions for the session_start event are
|
||||||
|
requesting the roster and broadcasting an initial
|
||||||
|
presence stanza.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
event -- An empty dictionary. The session_start
|
||||||
|
event does not provide any additional
|
||||||
|
data.
|
||||||
|
"""
|
||||||
|
self.send_presence()
|
||||||
|
self.get_roster()
|
||||||
|
|
||||||
|
def message(self, msg):
|
||||||
|
"""
|
||||||
|
Process incoming message stanzas. Be aware that this also
|
||||||
|
includes MUC messages and error messages. It is usually
|
||||||
|
a good idea to check the messages's type before processing
|
||||||
|
or sending replies.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
msg -- The received message stanza. See the documentation
|
||||||
|
for stanza objects and the Message stanza to see
|
||||||
|
how it may be used.
|
||||||
|
"""
|
||||||
|
if msg['type'] in ('chat', 'normal'):
|
||||||
|
msg.reply("Thanks for sending\n%(body)s" % msg).send()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Setup the command line arguments.
|
||||||
|
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 GTalkBot and register plugins. Note that while plugins may
|
||||||
|
# have interdependencies, the order in which you register them does
|
||||||
|
# not matter.
|
||||||
|
xmpp = GTalkBot(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.")
|
||||||
149
examples/ibb_transfer/ibb_receiver.py
Executable file
149
examples/ibb_transfer/ibb_receiver.py
Executable file
@@ -0,0 +1,149 @@
|
|||||||
|
#!/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 IBBReceiver(sleekxmpp.ClientXMPP):
|
||||||
|
|
||||||
|
"""
|
||||||
|
A basic example of creating and using an in-band bytestream.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, jid, password):
|
||||||
|
sleekxmpp.ClientXMPP.__init__(self, jid, password)
|
||||||
|
|
||||||
|
self.register_plugin('xep_0030') # Service Discovery
|
||||||
|
self.register_plugin('xep_0047', {
|
||||||
|
'accept_stream': self.accept_stream
|
||||||
|
}) # In-band Bytestreams
|
||||||
|
|
||||||
|
# The session_start event will be triggered when
|
||||||
|
# the bot establishes its connection with the server
|
||||||
|
# and the XML streams are ready for use. We want to
|
||||||
|
# listen for this event so that we we can initialize
|
||||||
|
# our roster.
|
||||||
|
self.add_event_handler("session_start", self.start)
|
||||||
|
|
||||||
|
self.add_event_handler("ibb_stream_start", self.stream_opened)
|
||||||
|
self.add_event_handler("ibb_stream_data", self.stream_data)
|
||||||
|
|
||||||
|
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 accept_stream(self, iq):
|
||||||
|
"""
|
||||||
|
Check that it is ok to accept a stream request.
|
||||||
|
|
||||||
|
Controlling stream acceptance can be done via either:
|
||||||
|
- setting 'auto_accept' to False in the plugin
|
||||||
|
configuration. The default is True.
|
||||||
|
- setting 'accept_stream' to a function which accepts
|
||||||
|
an Iq stanza as its argument, like this one.
|
||||||
|
|
||||||
|
The accept_stream function will be used if it exists, and the
|
||||||
|
auto_accept value will be used otherwise.
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def stream_opened(self, stream):
|
||||||
|
# NOTE: IBB streams are bi-directional, so the original sender is
|
||||||
|
# now the opened stream's receiver.
|
||||||
|
print('Stream opened: %s from ' % (stream.sid, stream.receiver))
|
||||||
|
|
||||||
|
# You could run a loop reading from the stream using stream.recv(),
|
||||||
|
# or use the ibb_stream_data event.
|
||||||
|
|
||||||
|
def stream_data(self, event):
|
||||||
|
print(event['data'])
|
||||||
|
|
||||||
|
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 = IBBReceiver(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.")
|
||||||
145
examples/ibb_transfer/ibb_sender.py
Executable file
145
examples/ibb_transfer/ibb_sender.py
Executable file
@@ -0,0 +1,145 @@
|
|||||||
|
#!/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 IBBSender(sleekxmpp.ClientXMPP):
|
||||||
|
|
||||||
|
"""
|
||||||
|
A basic example of creating and using an in-band bytestream.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, jid, password, receiver, filename):
|
||||||
|
sleekxmpp.ClientXMPP.__init__(self, jid, password)
|
||||||
|
|
||||||
|
self.receiver = receiver
|
||||||
|
self.filename = filename
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
|
||||||
|
# For the purpose of demonstration, we'll set a very small block
|
||||||
|
# size. The default block size is 4096. We'll also use a window
|
||||||
|
# allowing sending multiple blocks at a time; in this case, three
|
||||||
|
# block transfers may be in progress at any time.
|
||||||
|
stream = self['xep_0047'].open_stream(self.receiver)
|
||||||
|
|
||||||
|
with open(self.filename) as f:
|
||||||
|
data = f.read()
|
||||||
|
stream.sendall(data)
|
||||||
|
|
||||||
|
|
||||||
|
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", "--receiver", dest="receiver",
|
||||||
|
help="JID to use")
|
||||||
|
optp.add_option("-f", "--file", dest="filename",
|
||||||
|
help="JID 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: ")
|
||||||
|
if opts.receiver is None:
|
||||||
|
opts.receiver = raw_input("Receiver: ")
|
||||||
|
if opts.filename is None:
|
||||||
|
opts.filename = raw_input("File path: ")
|
||||||
|
|
||||||
|
# Setup the EchoBot and register plugins. Note that while plugins may
|
||||||
|
# have interdependencies, the order in which you register them does
|
||||||
|
# not matter.
|
||||||
|
xmpp = IBBSender(opts.jid, opts.password, opts.receiver, opts.filename)
|
||||||
|
xmpp.register_plugin('xep_0030') # Service Discovery
|
||||||
|
xmpp.register_plugin('xep_0004') # Data Forms
|
||||||
|
xmpp.register_plugin('xep_0047') # In-band Bytestreams
|
||||||
|
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.")
|
||||||
6
setup.py
6
setup.py
@@ -61,6 +61,7 @@ packages = [ 'sleekxmpp',
|
|||||||
'sleekxmpp/plugins/xep_0027',
|
'sleekxmpp/plugins/xep_0027',
|
||||||
'sleekxmpp/plugins/xep_0030',
|
'sleekxmpp/plugins/xep_0030',
|
||||||
'sleekxmpp/plugins/xep_0030/stanza',
|
'sleekxmpp/plugins/xep_0030/stanza',
|
||||||
|
'sleekxmpp/plugins/xep_0033',
|
||||||
'sleekxmpp/plugins/xep_0047',
|
'sleekxmpp/plugins/xep_0047',
|
||||||
'sleekxmpp/plugins/xep_0050',
|
'sleekxmpp/plugins/xep_0050',
|
||||||
'sleekxmpp/plugins/xep_0054',
|
'sleekxmpp/plugins/xep_0054',
|
||||||
@@ -71,6 +72,7 @@ packages = [ 'sleekxmpp',
|
|||||||
'sleekxmpp/plugins/xep_0077',
|
'sleekxmpp/plugins/xep_0077',
|
||||||
'sleekxmpp/plugins/xep_0078',
|
'sleekxmpp/plugins/xep_0078',
|
||||||
'sleekxmpp/plugins/xep_0080',
|
'sleekxmpp/plugins/xep_0080',
|
||||||
|
'sleekxmpp/plugins/xep_0084',
|
||||||
'sleekxmpp/plugins/xep_0085',
|
'sleekxmpp/plugins/xep_0085',
|
||||||
'sleekxmpp/plugins/xep_0086',
|
'sleekxmpp/plugins/xep_0086',
|
||||||
'sleekxmpp/plugins/xep_0092',
|
'sleekxmpp/plugins/xep_0092',
|
||||||
@@ -82,13 +84,17 @@ packages = [ 'sleekxmpp',
|
|||||||
'sleekxmpp/plugins/xep_0153',
|
'sleekxmpp/plugins/xep_0153',
|
||||||
'sleekxmpp/plugins/xep_0172',
|
'sleekxmpp/plugins/xep_0172',
|
||||||
'sleekxmpp/plugins/xep_0184',
|
'sleekxmpp/plugins/xep_0184',
|
||||||
|
'sleekxmpp/plugins/xep_0186',
|
||||||
|
'sleekxmpp/plugins/xep_0191',
|
||||||
'sleekxmpp/plugins/xep_0198',
|
'sleekxmpp/plugins/xep_0198',
|
||||||
'sleekxmpp/plugins/xep_0199',
|
'sleekxmpp/plugins/xep_0199',
|
||||||
'sleekxmpp/plugins/xep_0202',
|
'sleekxmpp/plugins/xep_0202',
|
||||||
'sleekxmpp/plugins/xep_0203',
|
'sleekxmpp/plugins/xep_0203',
|
||||||
|
'sleekxmpp/plugins/xep_0221',
|
||||||
'sleekxmpp/plugins/xep_0224',
|
'sleekxmpp/plugins/xep_0224',
|
||||||
'sleekxmpp/plugins/xep_0231',
|
'sleekxmpp/plugins/xep_0231',
|
||||||
'sleekxmpp/plugins/xep_0249',
|
'sleekxmpp/plugins/xep_0249',
|
||||||
|
'sleekxmpp/plugins/xep_0258',
|
||||||
'sleekxmpp/features',
|
'sleekxmpp/features',
|
||||||
'sleekxmpp/features/feature_mechanisms',
|
'sleekxmpp/features/feature_mechanisms',
|
||||||
'sleekxmpp/features/feature_mechanisms/stanza',
|
'sleekxmpp/features/feature_mechanisms/stanza',
|
||||||
|
|||||||
@@ -16,24 +16,24 @@ class APIWrapper(object):
|
|||||||
elif attr == 'settings':
|
elif attr == 'settings':
|
||||||
return self.api.settings[self.name]
|
return self.api.settings[self.name]
|
||||||
elif attr == 'register':
|
elif attr == 'register':
|
||||||
def curried_handler(handler, op, jid=None, node=None, default=False):
|
def partial(handler, op, jid=None, node=None, default=False):
|
||||||
register = getattr(self.api, attr)
|
register = getattr(self.api, attr)
|
||||||
return register(handler, self.name, op, jid, node, default)
|
return register(handler, self.name, op, jid, node, default)
|
||||||
return curried_handler
|
return partial
|
||||||
elif attr == 'register_default':
|
elif attr == 'register_default':
|
||||||
def curried_handler(handler, op, jid=None, node=None):
|
def partial(handler, op, jid=None, node=None):
|
||||||
return getattr(self.api, attr)(handler, self.name, op)
|
return getattr(self.api, attr)(handler, self.name, op)
|
||||||
return curried_handler
|
return partial
|
||||||
elif attr in ('run', 'restore_default', 'unregister'):
|
elif attr in ('run', 'restore_default', 'unregister'):
|
||||||
def curried_handler(*args, **kwargs):
|
def partial(*args, **kwargs):
|
||||||
return getattr(self.api, attr)(self.name, *args, **kwargs)
|
return getattr(self.api, attr)(self.name, *args, **kwargs)
|
||||||
return curried_handler
|
return partial
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __getitem__(self, attr):
|
def __getitem__(self, attr):
|
||||||
def curried_handler(jid=None, node=None, ifrom=None, args=None):
|
def partial(jid=None, node=None, ifrom=None, args=None):
|
||||||
return self.api.run(self.name, attr, jid, node, ifrom, args)
|
return self.api.run(self.name, attr, jid, node, ifrom, args)
|
||||||
return curried_handler
|
return partial
|
||||||
|
|
||||||
|
|
||||||
class APIRegistry(object):
|
class APIRegistry(object):
|
||||||
@@ -99,7 +99,7 @@ class APIRegistry(object):
|
|||||||
"""
|
"""
|
||||||
self._setup(ctype, op)
|
self._setup(ctype, op)
|
||||||
|
|
||||||
if jid in (None, ''):
|
if not jid:
|
||||||
jid = self.xmpp.boundjid
|
jid = self.xmpp.boundjid
|
||||||
if jid and not isinstance(jid, JID):
|
if jid and not isinstance(jid, JID):
|
||||||
jid = JID(jid)
|
jid = JID(jid)
|
||||||
@@ -113,7 +113,7 @@ class APIRegistry(object):
|
|||||||
else:
|
else:
|
||||||
jid = jid.full
|
jid = jid.full
|
||||||
else:
|
else:
|
||||||
if self.settings[ctype].get('client_bare', True):
|
if self.settings[ctype].get('client_bare', False):
|
||||||
jid = jid.bare
|
jid = jid.bare
|
||||||
else:
|
else:
|
||||||
jid = jid.full
|
jid = jid.full
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from __future__ import with_statement, unicode_literals
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
import threading
|
||||||
|
|
||||||
import sleekxmpp
|
import sleekxmpp
|
||||||
from sleekxmpp import plugins, features, roster
|
from sleekxmpp import plugins, features, roster
|
||||||
@@ -31,6 +32,7 @@ from sleekxmpp.xmlstream import XMLStream, JID
|
|||||||
from sleekxmpp.xmlstream import ET, register_stanza_plugin
|
from sleekxmpp.xmlstream import ET, register_stanza_plugin
|
||||||
from sleekxmpp.xmlstream.matcher import MatchXPath
|
from sleekxmpp.xmlstream.matcher import MatchXPath
|
||||||
from sleekxmpp.xmlstream.handler import Callback
|
from sleekxmpp.xmlstream.handler import Callback
|
||||||
|
from sleekxmpp.xmlstream.stanzabase import XML_NS
|
||||||
|
|
||||||
from sleekxmpp.features import *
|
from sleekxmpp.features import *
|
||||||
from sleekxmpp.plugins import PluginManager, register_plugin, load_plugin
|
from sleekxmpp.plugins import PluginManager, register_plugin, load_plugin
|
||||||
@@ -68,8 +70,11 @@ class BaseXMPP(XMLStream):
|
|||||||
|
|
||||||
#: The JabberID (JID) used by this connection.
|
#: The JabberID (JID) used by this connection.
|
||||||
self.boundjid = JID(jid)
|
self.boundjid = JID(jid)
|
||||||
|
|
||||||
self._expected_server_name = self.boundjid.host
|
self._expected_server_name = self.boundjid.host
|
||||||
|
|
||||||
|
self.session_bind_event = threading.Event()
|
||||||
|
|
||||||
#: A dictionary mapping plugin names to plugins.
|
#: A dictionary mapping plugin names to plugins.
|
||||||
self.plugin = PluginManager(self)
|
self.plugin = PluginManager(self)
|
||||||
|
|
||||||
@@ -86,13 +91,13 @@ class BaseXMPP(XMLStream):
|
|||||||
#: owner JIDs, as in the case for components. For clients
|
#: owner JIDs, as in the case for components. For clients
|
||||||
#: which only have a single JID, see :attr:`client_roster`.
|
#: which only have a single JID, see :attr:`client_roster`.
|
||||||
self.roster = roster.Roster(self)
|
self.roster = roster.Roster(self)
|
||||||
self.roster.add(self.boundjid.bare)
|
self.roster.add(self.boundjid)
|
||||||
|
|
||||||
#: The single roster for the bound JID. This is the
|
#: The single roster for the bound JID. This is the
|
||||||
#: equivalent of::
|
#: equivalent of::
|
||||||
#:
|
#:
|
||||||
#: self.roster[self.boundjid.bare]
|
#: self.roster[self.boundjid.bare]
|
||||||
self.client_roster = self.roster[self.boundjid.bare]
|
self.client_roster = self.roster[self.boundjid]
|
||||||
|
|
||||||
#: The distinction between clients and components can be
|
#: The distinction between clients and components can be
|
||||||
#: important, primarily for choosing how to handle the
|
#: important, primarily for choosing how to handle the
|
||||||
@@ -133,6 +138,7 @@ class BaseXMPP(XMLStream):
|
|||||||
Callback('Presence',
|
Callback('Presence',
|
||||||
MatchXPath("{%s}presence" % self.default_ns),
|
MatchXPath("{%s}presence" % self.default_ns),
|
||||||
self._handle_presence))
|
self._handle_presence))
|
||||||
|
|
||||||
self.register_handler(
|
self.register_handler(
|
||||||
Callback('Stream Error',
|
Callback('Stream Error',
|
||||||
MatchXPath("{%s}error" % self.stream_ns),
|
MatchXPath("{%s}error" % self.stream_ns),
|
||||||
@@ -180,6 +186,8 @@ class BaseXMPP(XMLStream):
|
|||||||
:param xml: The incoming stream's root element.
|
:param xml: The incoming stream's root element.
|
||||||
"""
|
"""
|
||||||
self.stream_id = xml.get('id', '')
|
self.stream_id = xml.get('id', '')
|
||||||
|
self.stream_version = xml.get('version', '')
|
||||||
|
self.peer_default_lang = xml.get('{%s}lang' % XML_NS, None)
|
||||||
|
|
||||||
def process(self, *args, **kwargs):
|
def process(self, *args, **kwargs):
|
||||||
"""Initialize plugins and begin processing the XML stream.
|
"""Initialize plugins and begin processing the XML stream.
|
||||||
@@ -272,7 +280,9 @@ class BaseXMPP(XMLStream):
|
|||||||
|
|
||||||
def Message(self, *args, **kwargs):
|
def Message(self, *args, **kwargs):
|
||||||
"""Create a Message stanza associated with this stream."""
|
"""Create a Message stanza associated with this stream."""
|
||||||
return Message(self, *args, **kwargs)
|
msg = Message(self, *args, **kwargs)
|
||||||
|
msg['lang'] = self.default_lang
|
||||||
|
return msg
|
||||||
|
|
||||||
def Iq(self, *args, **kwargs):
|
def Iq(self, *args, **kwargs):
|
||||||
"""Create an Iq stanza associated with this stream."""
|
"""Create an Iq stanza associated with this stream."""
|
||||||
@@ -280,7 +290,9 @@ class BaseXMPP(XMLStream):
|
|||||||
|
|
||||||
def Presence(self, *args, **kwargs):
|
def Presence(self, *args, **kwargs):
|
||||||
"""Create a Presence stanza associated with this stream."""
|
"""Create a Presence stanza associated with this stream."""
|
||||||
return Presence(self, *args, **kwargs)
|
pres = Presence(self, *args, **kwargs)
|
||||||
|
pres['lang'] = self.default_lang
|
||||||
|
return pres
|
||||||
|
|
||||||
def make_iq(self, id=0, ifrom=None, ito=None, itype=None, iquery=None):
|
def make_iq(self, id=0, ifrom=None, ito=None, itype=None, iquery=None):
|
||||||
"""Create a new Iq stanza with a given Id and from JID.
|
"""Create a new Iq stanza with a given Id and from JID.
|
||||||
@@ -529,18 +541,8 @@ class BaseXMPP(XMLStream):
|
|||||||
:param pfrom: The sender of the presence.
|
:param pfrom: The sender of the presence.
|
||||||
:param pnick: Optional nickname of the presence's sender.
|
:param pnick: Optional nickname of the presence's sender.
|
||||||
"""
|
"""
|
||||||
# Python2.6 chokes on Unicode strings for dict keys.
|
self.make_presence(pshow, pstatus, ppriority, pto,
|
||||||
args = {str('pto'): pto,
|
ptype, pfrom, pnick).send()
|
||||||
str('ptype'): ptype,
|
|
||||||
str('pshow'): pshow,
|
|
||||||
str('pstatus'): pstatus,
|
|
||||||
str('ppriority'): ppriority,
|
|
||||||
str('pnick'): pnick}
|
|
||||||
|
|
||||||
if self.is_component:
|
|
||||||
self.roster[pfrom].send_presence(**args)
|
|
||||||
else:
|
|
||||||
self.client_roster.send_presence(**args)
|
|
||||||
|
|
||||||
def send_presence_subscription(self, pto, pfrom=None,
|
def send_presence_subscription(self, pto, pfrom=None,
|
||||||
ptype='subscribe', pnick=None):
|
ptype='subscribe', pnick=None):
|
||||||
@@ -554,14 +556,10 @@ class BaseXMPP(XMLStream):
|
|||||||
:param ptype: The type of presence, such as ``'subscribe'``.
|
:param ptype: The type of presence, such as ``'subscribe'``.
|
||||||
:param pnick: Optional nickname of the presence's sender.
|
:param pnick: Optional nickname of the presence's sender.
|
||||||
"""
|
"""
|
||||||
presence = self.makePresence(ptype=ptype,
|
self.make_presence(ptype=ptype,
|
||||||
pfrom=pfrom,
|
pfrom=pfrom,
|
||||||
pto=self.getjidbare(pto))
|
pto=JID(pto).bare,
|
||||||
if pnick:
|
pnick=pnick).send()
|
||||||
nick = ET.Element('{http://jabber.org/protocol/nick}nick')
|
|
||||||
nick.text = pnick
|
|
||||||
presence.append(nick)
|
|
||||||
presence.send()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def jid(self):
|
def jid(self):
|
||||||
@@ -661,27 +659,45 @@ class BaseXMPP(XMLStream):
|
|||||||
def _handle_disconnected(self, event):
|
def _handle_disconnected(self, event):
|
||||||
"""When disconnected, reset the roster"""
|
"""When disconnected, reset the roster"""
|
||||||
self.roster.reset()
|
self.roster.reset()
|
||||||
|
self.session_bind_event.clear()
|
||||||
|
|
||||||
def _handle_stream_error(self, error):
|
def _handle_stream_error(self, error):
|
||||||
self.event('stream_error', error)
|
self.event('stream_error', error)
|
||||||
|
|
||||||
|
if error['condition'] == 'see-other-host':
|
||||||
|
other_host = error['see_other_host']
|
||||||
|
|
||||||
|
host = other_host
|
||||||
|
port = 5222
|
||||||
|
|
||||||
|
if '[' in other_host and ']' in other_host:
|
||||||
|
host = other_host.split(']')[0][1:]
|
||||||
|
elif ':' in other_host:
|
||||||
|
host = other_host.split(':')[0]
|
||||||
|
|
||||||
|
port_sec = other_host.split(']')[-1]
|
||||||
|
if ':' in port_sec:
|
||||||
|
port = int(port_sec.split(':')[1])
|
||||||
|
|
||||||
|
self.address = (host, port)
|
||||||
|
self.default_domain = host
|
||||||
|
self.dns_records = None
|
||||||
|
self.reconnect_delay = None
|
||||||
|
self.reconnect()
|
||||||
|
|
||||||
def _handle_message(self, msg):
|
def _handle_message(self, msg):
|
||||||
"""Process incoming message stanzas."""
|
"""Process incoming message stanzas."""
|
||||||
if not self.is_component and not msg['to'].bare:
|
if not self.is_component and not msg['to'].bare:
|
||||||
msg['to'] = self.boundjid
|
msg['to'] = self.boundjid
|
||||||
self.event('message', msg)
|
self.event('message', msg)
|
||||||
|
|
||||||
def _handle_available(self, presence):
|
def _handle_available(self, pres):
|
||||||
pto = presence['to'].bare
|
self.roster[pres['to']][pres['from']].handle_available(pres)
|
||||||
pfrom = presence['from'].bare
|
|
||||||
self.roster[pto][pfrom].handle_available(presence)
|
|
||||||
|
|
||||||
def _handle_unavailable(self, presence):
|
def _handle_unavailable(self, pres):
|
||||||
pto = presence['to'].bare
|
self.roster[pres['to']][pres['from']].handle_unavailable(pres)
|
||||||
pfrom = presence['from'].bare
|
|
||||||
self.roster[pto][pfrom].handle_unavailable(presence)
|
|
||||||
|
|
||||||
def _handle_new_subscription(self, stanza):
|
def _handle_new_subscription(self, pres):
|
||||||
"""Attempt to automatically handle subscription requests.
|
"""Attempt to automatically handle subscription requests.
|
||||||
|
|
||||||
Subscriptions will be approved if the request is from
|
Subscriptions will be approved if the request is from
|
||||||
@@ -693,8 +709,8 @@ class BaseXMPP(XMLStream):
|
|||||||
If a subscription is accepted, a request for a mutual
|
If a subscription is accepted, a request for a mutual
|
||||||
subscription will be sent if :attr:`auto_subscribe` is ``True``.
|
subscription will be sent if :attr:`auto_subscribe` is ``True``.
|
||||||
"""
|
"""
|
||||||
roster = self.roster[stanza['to'].bare]
|
roster = self.roster[pres['to']]
|
||||||
item = self.roster[stanza['to'].bare][stanza['from'].bare]
|
item = self.roster[pres['to']][pres['from']]
|
||||||
if item['whitelisted']:
|
if item['whitelisted']:
|
||||||
item.authorize()
|
item.authorize()
|
||||||
elif roster.auto_authorize:
|
elif roster.auto_authorize:
|
||||||
@@ -704,30 +720,20 @@ class BaseXMPP(XMLStream):
|
|||||||
elif roster.auto_authorize == False:
|
elif roster.auto_authorize == False:
|
||||||
item.unauthorize()
|
item.unauthorize()
|
||||||
|
|
||||||
def _handle_removed_subscription(self, presence):
|
def _handle_removed_subscription(self, pres):
|
||||||
pto = presence['to'].bare
|
self.roster[pres['to']][pres['from']].handle_unauthorize(pres)
|
||||||
pfrom = presence['from'].bare
|
|
||||||
self.roster[pto][pfrom].unauthorize()
|
|
||||||
|
|
||||||
def _handle_subscribe(self, presence):
|
def _handle_subscribe(self, pres):
|
||||||
pto = presence['to'].bare
|
self.roster[pres['to']][pres['from']].handle_subscribe(pres)
|
||||||
pfrom = presence['from'].bare
|
|
||||||
self.roster[pto][pfrom].handle_subscribe(presence)
|
|
||||||
|
|
||||||
def _handle_subscribed(self, presence):
|
def _handle_subscribed(self, pres):
|
||||||
pto = presence['to'].bare
|
self.roster[pres['to']][pres['from']].handle_subscribed(pres)
|
||||||
pfrom = presence['from'].bare
|
|
||||||
self.roster[pto][pfrom].handle_subscribed(presence)
|
|
||||||
|
|
||||||
def _handle_unsubscribe(self, presence):
|
def _handle_unsubscribe(self, pres):
|
||||||
pto = presence['to'].bare
|
self.roster[pres['to']][pres['from']].handle_unsubscribe(pres)
|
||||||
pfrom = presence['from'].bare
|
|
||||||
self.roster[pto][pfrom].handle_unsubscribe(presence)
|
|
||||||
|
|
||||||
def _handle_unsubscribed(self, presence):
|
def _handle_unsubscribed(self, pres):
|
||||||
pto = presence['to'].bare
|
self.roster[pres['to']][pres['from']].handle_unsubscribed(pres)
|
||||||
pfrom = presence['from'].bare
|
|
||||||
self.roster[pto][pfrom].handle_unsubscribed(presence)
|
|
||||||
|
|
||||||
def _handle_presence(self, presence):
|
def _handle_presence(self, presence):
|
||||||
"""Process incoming presence stanzas.
|
"""Process incoming presence stanzas.
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ class ClientXMPP(BaseXMPP):
|
|||||||
:param escape_quotes: **Deprecated.**
|
:param escape_quotes: **Deprecated.**
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, jid, password, ssl=False, plugin_config={},
|
def __init__(self, jid, password, plugin_config={}, plugin_whitelist=[],
|
||||||
plugin_whitelist=[], escape_quotes=True, sasl_mech=None):
|
escape_quotes=True, sasl_mech=None, lang='en'):
|
||||||
BaseXMPP.__init__(self, jid, 'jabber:client')
|
BaseXMPP.__init__(self, jid, 'jabber:client')
|
||||||
|
|
||||||
self.set_jid(jid)
|
self.set_jid(jid)
|
||||||
@@ -69,15 +69,18 @@ class ClientXMPP(BaseXMPP):
|
|||||||
self.plugin_config = plugin_config
|
self.plugin_config = plugin_config
|
||||||
self.plugin_whitelist = plugin_whitelist
|
self.plugin_whitelist = plugin_whitelist
|
||||||
self.default_port = 5222
|
self.default_port = 5222
|
||||||
|
self.default_lang = lang
|
||||||
|
|
||||||
self.credentials = {}
|
self.credentials = {}
|
||||||
|
|
||||||
self.password = password
|
self.password = password
|
||||||
|
|
||||||
self.stream_header = "<stream:stream to='%s' %s %s version='1.0'>" % (
|
self.stream_header = "<stream:stream to='%s' %s %s %s %s>" % (
|
||||||
self.boundjid.host,
|
self.boundjid.host,
|
||||||
"xmlns:stream='%s'" % self.stream_ns,
|
"xmlns:stream='%s'" % self.stream_ns,
|
||||||
"xmlns='%s'" % self.default_ns)
|
"xmlns='%s'" % self.default_ns,
|
||||||
|
"xml:lang='%s'" % self.default_lang,
|
||||||
|
"version='1.0'")
|
||||||
self.stream_footer = "</stream:stream>"
|
self.stream_footer = "</stream:stream>"
|
||||||
|
|
||||||
self.features = set()
|
self.features = set()
|
||||||
@@ -170,6 +173,12 @@ class ClientXMPP(BaseXMPP):
|
|||||||
self._stream_feature_order.append((order, name))
|
self._stream_feature_order.append((order, name))
|
||||||
self._stream_feature_order.sort()
|
self._stream_feature_order.sort()
|
||||||
|
|
||||||
|
def unregister_feature(self, name, order):
|
||||||
|
if name in self._stream_feature_handlers:
|
||||||
|
del self._stream_feature_handlers[name]
|
||||||
|
self._stream_feature_order.remove((order, name))
|
||||||
|
self._stream_feature_order.sort()
|
||||||
|
|
||||||
def update_roster(self, jid, name=None, subscription=None, groups=[],
|
def update_roster(self, jid, name=None, subscription=None, groups=[],
|
||||||
block=True, timeout=None, callback=None):
|
block=True, timeout=None, callback=None):
|
||||||
"""Add or change a roster item.
|
"""Add or change a roster item.
|
||||||
@@ -267,8 +276,9 @@ class ClientXMPP(BaseXMPP):
|
|||||||
roster = self.client_roster
|
roster = self.client_roster
|
||||||
if iq['roster']['ver']:
|
if iq['roster']['ver']:
|
||||||
roster.version = iq['roster']['ver']
|
roster.version = iq['roster']['ver']
|
||||||
for jid in iq['roster']['items']:
|
items = iq['roster']['items']
|
||||||
item = iq['roster']['items'][jid]
|
for jid in items:
|
||||||
|
item = items[jid]
|
||||||
roster[jid]['name'] = item['name']
|
roster[jid]['name'] = item['name']
|
||||||
roster[jid]['groups'] = item['groups']
|
roster[jid]['groups'] = item['groups']
|
||||||
roster[jid]['from'] = item['subscription'] in ['from', 'both']
|
roster[jid]['from'] = item['subscription'] in ['from', 'both']
|
||||||
|
|||||||
@@ -156,10 +156,10 @@ class ComponentXMPP(BaseXMPP):
|
|||||||
|
|
||||||
:param xml: The reply handshake stanza.
|
:param xml: The reply handshake stanza.
|
||||||
"""
|
"""
|
||||||
|
self.session_bind_event.set()
|
||||||
self.session_started_event.set()
|
self.session_started_event.set()
|
||||||
|
self.event("session_bind", self.xmpp.boundjid, direct=True)
|
||||||
self.event("session_start")
|
self.event("session_start")
|
||||||
|
|
||||||
def _handle_probe(self, presence):
|
def _handle_probe(self, pres):
|
||||||
pto = presence['to'].bare
|
self.roster[pres['to']][pres['from']].handle_probe(pres)
|
||||||
pfrom = presence['from'].bare
|
|
||||||
self.roster[pto][pfrom].handle_probe(presence)
|
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ class IqTimeout(XMPPError):
|
|||||||
#: did not arrive before the timeout expired.
|
#: did not arrive before the timeout expired.
|
||||||
self.iq = iq
|
self.iq = iq
|
||||||
|
|
||||||
|
|
||||||
class IqError(XMPPError):
|
class IqError(XMPPError):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ class FeatureBind(BasePlugin):
|
|||||||
self.xmpp.set_jid(response['bind']['jid'])
|
self.xmpp.set_jid(response['bind']['jid'])
|
||||||
self.xmpp.bound = True
|
self.xmpp.bound = True
|
||||||
self.xmpp.event('session_bind', self.xmpp.boundjid, direct=True)
|
self.xmpp.event('session_bind', self.xmpp.boundjid, direct=True)
|
||||||
|
self.xmpp.session_bind_event.set()
|
||||||
|
|
||||||
self.xmpp.features.add('bind')
|
self.xmpp.features.add('bind')
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class Failure(StanzaBase):
|
|||||||
|
|
||||||
def get_condition(self):
|
def get_condition(self):
|
||||||
"""Return the condition element's name."""
|
"""Return the condition element's name."""
|
||||||
for child in self.xml.getchildren():
|
for child in self.xml:
|
||||||
if "{%s}" % self.namespace in child.tag:
|
if "{%s}" % self.namespace in child.tag:
|
||||||
cond = child.tag.split('}', 1)[-1]
|
cond = child.tag.split('}', 1)[-1]
|
||||||
if cond in self.conditions:
|
if cond in self.conditions:
|
||||||
@@ -68,7 +68,7 @@ class Failure(StanzaBase):
|
|||||||
|
|
||||||
def del_condition(self):
|
def del_condition(self):
|
||||||
"""Remove the condition element."""
|
"""Remove the condition element."""
|
||||||
for child in self.xml.getchildren():
|
for child in self.xml:
|
||||||
if "{%s}" % self.condition_ns in child.tag:
|
if "{%s}" % self.condition_ns in child.tag:
|
||||||
tag = child.tag.split('}', 1)[-1]
|
tag = child.tag.split('}', 1)[-1]
|
||||||
if tag in self.conditions:
|
if tag in self.conditions:
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ __all__ = [
|
|||||||
# 'xep_0078', # Non-SASL auth. Don't automatically load
|
# 'xep_0078', # Non-SASL auth. Don't automatically load
|
||||||
'xep_0080', # User Location
|
'xep_0080', # User Location
|
||||||
'xep_0082', # XMPP Date and Time Profiles
|
'xep_0082', # XMPP Date and Time Profiles
|
||||||
|
'xep_0084', # User Avatar
|
||||||
'xep_0085', # Chat State Notifications
|
'xep_0085', # Chat State Notifications
|
||||||
'xep_0086', # Legacy Error Codes
|
'xep_0086', # Legacy Error Codes
|
||||||
'xep_0092', # Software Version
|
'xep_0092', # Software Version
|
||||||
@@ -44,11 +45,20 @@ __all__ = [
|
|||||||
'xep_0163', # Personal Eventing Protocol
|
'xep_0163', # Personal Eventing Protocol
|
||||||
'xep_0172', # User Nickname
|
'xep_0172', # User Nickname
|
||||||
'xep_0184', # Message Receipts
|
'xep_0184', # Message Receipts
|
||||||
|
'xep_0186', # Invisible Command
|
||||||
|
'xep_0191', # Blocking Command
|
||||||
'xep_0198', # Stream Management
|
'xep_0198', # Stream Management
|
||||||
'xep_0199', # Ping
|
'xep_0199', # Ping
|
||||||
'xep_0202', # Entity Time
|
'xep_0202', # Entity Time
|
||||||
'xep_0203', # Delayed Delivery
|
'xep_0203', # Delayed Delivery
|
||||||
|
'xep_0221', # Data Forms Media Element
|
||||||
|
'xep_0222', # Persistent Storage of Public Data via Pubsub
|
||||||
|
'xep_0223', # Persistent Storage of Private Data via Pubsub
|
||||||
'xep_0224', # Attention
|
'xep_0224', # Attention
|
||||||
'xep_0231', # Bits of Binary
|
'xep_0231', # Bits of Binary
|
||||||
'xep_0249', # Direct MUC Invitations
|
'xep_0249', # Direct MUC Invitations
|
||||||
|
'xep_0256', # Last Activity in Presence
|
||||||
|
'xep_0258', # Security Labels in XMPP
|
||||||
|
'xep_0270', # XMPP Compliance Suites 2010
|
||||||
|
'xep_0302', # XMPP Compliance Suites 2012
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ def load_plugin(name, module=None):
|
|||||||
module = 'sleekxmpp.plugins.%s' % name
|
module = 'sleekxmpp.plugins.%s' % name
|
||||||
__import__(module)
|
__import__(module)
|
||||||
mod = sys.modules[module]
|
mod = sys.modules[module]
|
||||||
except:
|
except ImportError:
|
||||||
module = 'sleekxmpp.features.%s' % name
|
module = 'sleekxmpp.features.%s' % name
|
||||||
__import__(module)
|
__import__(module)
|
||||||
mod = sys.modules[module]
|
mod = sys.modules[module]
|
||||||
@@ -103,7 +103,7 @@ def load_plugin(name, module=None):
|
|||||||
# we can work around dependency issues.
|
# we can work around dependency issues.
|
||||||
plugin.old_style = True
|
plugin.old_style = True
|
||||||
register_plugin(plugin, name)
|
register_plugin(plugin, name)
|
||||||
except:
|
except ImportError:
|
||||||
log.exception("Unable to load plugin: %s", name)
|
log.exception("Unable to load plugin: %s", name)
|
||||||
|
|
||||||
|
|
||||||
@@ -167,8 +167,7 @@ class PluginManager(object):
|
|||||||
self._plugins[name] = plugin
|
self._plugins[name] = plugin
|
||||||
for dep in plugin.dependencies:
|
for dep in plugin.dependencies:
|
||||||
self.enable(dep, enabled=enabled)
|
self.enable(dep, enabled=enabled)
|
||||||
plugin.plugin_init()
|
plugin._init()
|
||||||
log.debug("Loaded Plugin: %s", plugin.description)
|
|
||||||
|
|
||||||
if top_level:
|
if top_level:
|
||||||
for name in enabled:
|
for name in enabled:
|
||||||
@@ -229,7 +228,7 @@ class PluginManager(object):
|
|||||||
raise PluginNotFound(name)
|
raise PluginNotFound(name)
|
||||||
for dep in PLUGIN_DEPENDENTS[name]:
|
for dep in PLUGIN_DEPENDENTS[name]:
|
||||||
self.disable(dep, _disabled)
|
self.disable(dep, _disabled)
|
||||||
plugin.plugin_end()
|
plugin._end()
|
||||||
if name in self._enabled:
|
if name in self._enabled:
|
||||||
self._enabled.remove(name)
|
self._enabled.remove(name)
|
||||||
del self._plugins[name]
|
del self._plugins[name]
|
||||||
@@ -282,6 +281,28 @@ class BasePlugin(object):
|
|||||||
#: configuration settings will be provided as a dictionary.
|
#: configuration settings will be provided as a dictionary.
|
||||||
self.config = config if config is not None else {}
|
self.config = config if config is not None else {}
|
||||||
|
|
||||||
|
def _init(self):
|
||||||
|
"""Initialize plugin state, such as registering event handlers.
|
||||||
|
|
||||||
|
Also sets up required event handlers.
|
||||||
|
"""
|
||||||
|
if self.xmpp is not None:
|
||||||
|
self.xmpp.add_event_handler('session_bind', self.session_bind)
|
||||||
|
if self.xmpp.session_bind_event.is_set():
|
||||||
|
self.session_bind(self.xmpp.boundjid.full)
|
||||||
|
self.plugin_init()
|
||||||
|
log.debug('Loaded Plugin: %s', self.description)
|
||||||
|
|
||||||
|
def _end(self):
|
||||||
|
"""Cleanup plugin state, and prepare for plugin removal.
|
||||||
|
|
||||||
|
Also removes required event handlers.
|
||||||
|
"""
|
||||||
|
if self.xmpp is not None:
|
||||||
|
self.xmpp.del_event_handler('session_bind', self.session_bind)
|
||||||
|
self.plugin_end()
|
||||||
|
log.debug('Disabled Plugin: %s' % self.description)
|
||||||
|
|
||||||
def plugin_init(self):
|
def plugin_init(self):
|
||||||
"""Initialize plugin state, such as registering event handlers."""
|
"""Initialize plugin state, such as registering event handlers."""
|
||||||
pass
|
pass
|
||||||
@@ -290,6 +311,10 @@ class BasePlugin(object):
|
|||||||
"""Cleanup plugin state, and prepare for plugin removal."""
|
"""Cleanup plugin state, and prepare for plugin removal."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
|
"""Initialize plugin state based on the bound JID."""
|
||||||
|
pass
|
||||||
|
|
||||||
def post_init(self):
|
def post_init(self):
|
||||||
"""Initialize any cross-plugin state.
|
"""Initialize any cross-plugin state.
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class XEP_0004(BasePlugin):
|
|||||||
stanza = stanza
|
stanza = stanza
|
||||||
|
|
||||||
def plugin_init(self):
|
def plugin_init(self):
|
||||||
self.xmpp.registerHandler(
|
self.xmpp.register_handler(
|
||||||
Callback('Data Form',
|
Callback('Data Form',
|
||||||
StanzaPath('message/form'),
|
StanzaPath('message/form'),
|
||||||
self.handle_form))
|
self.handle_form))
|
||||||
@@ -36,6 +36,11 @@ class XEP_0004(BasePlugin):
|
|||||||
register_stanza_plugin(Form, FormField, iterable=True)
|
register_stanza_plugin(Form, FormField, iterable=True)
|
||||||
register_stanza_plugin(Message, Form)
|
register_stanza_plugin(Message, Form)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp.remove_handler('Data Form')
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature='jabber:x:data')
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp['xep_0030'].add_feature('jabber:x:data')
|
self.xmpp['xep_0030'].add_feature('jabber:x:data')
|
||||||
|
|
||||||
def make_form(self, ftype='form', title='', instructions=''):
|
def make_form(self, ftype='form', title='', instructions=''):
|
||||||
|
|||||||
@@ -37,13 +37,11 @@ class XEP_0012(BasePlugin):
|
|||||||
|
|
||||||
self._last_activities = {}
|
self._last_activities = {}
|
||||||
|
|
||||||
self.xmpp.registerHandler(
|
self.xmpp.register_handler(
|
||||||
Callback('Last Activity',
|
Callback('Last Activity',
|
||||||
StanzaPath('iq@type=get/last_activity'),
|
StanzaPath('iq@type=get/last_activity'),
|
||||||
self._handle_get_last_activity))
|
self._handle_get_last_activity))
|
||||||
|
|
||||||
self.xmpp.plugin['xep_0030'].add_feature('jabber:iq:last')
|
|
||||||
|
|
||||||
self.api.register(self._default_get_last_activity,
|
self.api.register(self._default_get_last_activity,
|
||||||
'get_last_activity',
|
'get_last_activity',
|
||||||
default=True)
|
default=True)
|
||||||
@@ -54,6 +52,13 @@ class XEP_0012(BasePlugin):
|
|||||||
'del_last_activity',
|
'del_last_activity',
|
||||||
default=True)
|
default=True)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp.remove_handler('Last Activity')
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature='jabber:iq:last')
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
|
self.xmpp['xep_0030'].add_feature('jabber:iq:last')
|
||||||
|
|
||||||
def begin_idle(self, jid=None, status=None):
|
def begin_idle(self, jid=None, status=None):
|
||||||
self.set_last_activity(jid, 0, status)
|
self.set_last_activity(jid, 0, status)
|
||||||
|
|
||||||
|
|||||||
@@ -79,9 +79,17 @@ class XEP_0027(BasePlugin):
|
|||||||
StanzaPath('message/encrypted'),
|
StanzaPath('message/encrypted'),
|
||||||
self._handle_encrypted_message))
|
self._handle_encrypted_message))
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp.remove_handler('Encrypted Message')
|
||||||
|
self.xmpp.remove_handler('Signed Presence')
|
||||||
|
self.xmpp.del_filter('out', self._sign_presence)
|
||||||
|
self.xmpp.del_event_handler('unverified_signed_presence',
|
||||||
|
self._handle_unverified_signed_presence)
|
||||||
|
|
||||||
def _sign_presence(self, stanza):
|
def _sign_presence(self, stanza):
|
||||||
if isinstance(stanza, Presence):
|
if isinstance(stanza, Presence):
|
||||||
if stanza['type'] == 'available' or stanza['type'] in Presence.showtypes:
|
if stanza['type'] == 'available' or \
|
||||||
|
stanza['type'] in Presence.showtypes:
|
||||||
stanza['signed'] = stanza['status']
|
stanza['signed'] = stanza['status']
|
||||||
return stanza
|
return stanza
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,3 @@ class Encrypted(ElementBase):
|
|||||||
if self.xml.text:
|
if self.xml.text:
|
||||||
return xmpp['xep_0027'].decrypt(self.xml.text, parent['to'])
|
return xmpp['xep_0027'].decrypt(self.xml.text, parent['to'])
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -622,11 +622,7 @@ class XEP_0030(BasePlugin):
|
|||||||
if iq['type'] == 'get':
|
if iq['type'] == 'get':
|
||||||
log.debug("Received disco info query from " + \
|
log.debug("Received disco info query from " + \
|
||||||
"<%s> to <%s>.", iq['from'], iq['to'])
|
"<%s> to <%s>.", iq['from'], iq['to'])
|
||||||
if self.xmpp.is_component:
|
info = self.api['get_info'](iq['to'],
|
||||||
jid = iq['to'].full
|
|
||||||
else:
|
|
||||||
jid = iq['to'].bare
|
|
||||||
info = self.api['get_info'](jid,
|
|
||||||
iq['disco_info']['node'],
|
iq['disco_info']['node'],
|
||||||
iq['from'],
|
iq['from'],
|
||||||
iq)
|
iq)
|
||||||
@@ -649,7 +645,7 @@ class XEP_0030(BasePlugin):
|
|||||||
ito = iq['to'].full
|
ito = iq['to'].full
|
||||||
else:
|
else:
|
||||||
ito = None
|
ito = None
|
||||||
self.api['cache_info'](iq['from'].full,
|
self.api['cache_info'](iq['from'],
|
||||||
iq['disco_info']['node'],
|
iq['disco_info']['node'],
|
||||||
ito,
|
ito,
|
||||||
iq)
|
iq)
|
||||||
@@ -667,13 +663,9 @@ class XEP_0030(BasePlugin):
|
|||||||
if iq['type'] == 'get':
|
if iq['type'] == 'get':
|
||||||
log.debug("Received disco items query from " + \
|
log.debug("Received disco items query from " + \
|
||||||
"<%s> to <%s>.", iq['from'], iq['to'])
|
"<%s> to <%s>.", iq['from'], iq['to'])
|
||||||
if self.xmpp.is_component:
|
items = self.api['get_items'](iq['to'],
|
||||||
jid = iq['to'].full
|
|
||||||
else:
|
|
||||||
jid = iq['to'].bare
|
|
||||||
items = self.api['get_items'](jid,
|
|
||||||
iq['disco_items']['node'],
|
iq['disco_items']['node'],
|
||||||
iq['from'].full,
|
iq['from'],
|
||||||
iq)
|
iq)
|
||||||
if isinstance(items, Iq):
|
if isinstance(items, Iq):
|
||||||
items.send()
|
items.send()
|
||||||
|
|||||||
@@ -182,11 +182,6 @@ class StaticDisco(object):
|
|||||||
data = {'local': data.get('local', False),
|
data = {'local': data.get('local', False),
|
||||||
'cached': data.get('cached', True)}
|
'cached': data.get('cached', True)}
|
||||||
|
|
||||||
if node in (None, ''):
|
|
||||||
info = self.caps.get_caps(jid)
|
|
||||||
if info and identity in info['identities']:
|
|
||||||
return True
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
info = self.disco.get_info(jid=jid, node=node,
|
info = self.disco.get_info(jid=jid, node=node,
|
||||||
ifrom=ifrom, **data)
|
ifrom=ifrom, **data)
|
||||||
@@ -242,7 +237,7 @@ class StaticDisco(object):
|
|||||||
with self.lock:
|
with self.lock:
|
||||||
if not self.node_exists(jid, node):
|
if not self.node_exists(jid, node):
|
||||||
if not node:
|
if not node:
|
||||||
return DiscoInfo()
|
return DiscoItems()
|
||||||
else:
|
else:
|
||||||
raise XMPPError(condition='item-not-found')
|
raise XMPPError(condition='item-not-found')
|
||||||
else:
|
else:
|
||||||
@@ -429,9 +424,6 @@ class StaticDisco(object):
|
|||||||
The data parameter is not used.
|
The data parameter is not used.
|
||||||
"""
|
"""
|
||||||
with self.lock:
|
with self.lock:
|
||||||
if isinstance(jid, JID):
|
|
||||||
jid = jid.full
|
|
||||||
|
|
||||||
if not self.node_exists(jid, node, ifrom):
|
if not self.node_exists(jid, node, ifrom):
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -1,167 +0,0 @@
|
|||||||
"""
|
|
||||||
SleekXMPP: The Sleek XMPP Library
|
|
||||||
Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
|
|
||||||
This file is part of SleekXMPP.
|
|
||||||
|
|
||||||
See the file LICENSE for copying permission.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import logging
|
|
||||||
from sleekxmpp import Message
|
|
||||||
from sleekxmpp.xmlstream.handler.callback import Callback
|
|
||||||
from sleekxmpp.xmlstream.matcher.xpath import MatchXPath
|
|
||||||
from sleekxmpp.xmlstream import register_stanza_plugin, ElementBase, ET, JID
|
|
||||||
from sleekxmpp.plugins import BasePlugin, register_plugin
|
|
||||||
|
|
||||||
|
|
||||||
class Addresses(ElementBase):
|
|
||||||
namespace = 'http://jabber.org/protocol/address'
|
|
||||||
name = 'addresses'
|
|
||||||
plugin_attrib = 'addresses'
|
|
||||||
interfaces = set(('addresses', 'bcc', 'cc', 'noreply', 'replyroom', 'replyto', 'to'))
|
|
||||||
|
|
||||||
def addAddress(self, atype='to', jid='', node='', uri='', desc='', delivered=False):
|
|
||||||
address = Address(parent=self)
|
|
||||||
address['type'] = atype
|
|
||||||
address['jid'] = jid
|
|
||||||
address['node'] = node
|
|
||||||
address['uri'] = uri
|
|
||||||
address['desc'] = desc
|
|
||||||
address['delivered'] = delivered
|
|
||||||
return address
|
|
||||||
|
|
||||||
def getAddresses(self, atype=None):
|
|
||||||
addresses = []
|
|
||||||
for addrXML in self.xml.findall('{%s}address' % Address.namespace):
|
|
||||||
# ElementTree 1.2.6 does not support [@attr='value'] in findall
|
|
||||||
if atype is None or addrXML.attrib.get('type') == atype:
|
|
||||||
addresses.append(Address(xml=addrXML, parent=None))
|
|
||||||
return addresses
|
|
||||||
|
|
||||||
def setAddresses(self, addresses, set_type=None):
|
|
||||||
self.delAddresses(set_type)
|
|
||||||
for addr in addresses:
|
|
||||||
addr = dict(addr)
|
|
||||||
# Remap 'type' to 'atype' to match the add method
|
|
||||||
if set_type is not None:
|
|
||||||
addr['type'] = set_type
|
|
||||||
curr_type = addr.get('type', None)
|
|
||||||
if curr_type is not None:
|
|
||||||
del addr['type']
|
|
||||||
addr['atype'] = curr_type
|
|
||||||
self.addAddress(**addr)
|
|
||||||
|
|
||||||
def delAddresses(self, atype=None):
|
|
||||||
if atype is None:
|
|
||||||
return
|
|
||||||
for addrXML in self.xml.findall('{%s}address' % Address.namespace):
|
|
||||||
# ElementTree 1.2.6 does not support [@attr='value'] in findall
|
|
||||||
if addrXML.attrib.get('type') == atype:
|
|
||||||
self.xml.remove(addrXML)
|
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
|
|
||||||
def delBcc(self):
|
|
||||||
self.delAddresses('bcc')
|
|
||||||
|
|
||||||
def delCc(self):
|
|
||||||
self.delAddresses('cc')
|
|
||||||
|
|
||||||
def delNoreply(self):
|
|
||||||
self.delAddresses('noreply')
|
|
||||||
|
|
||||||
def delReplyroom(self):
|
|
||||||
self.delAddresses('replyroom')
|
|
||||||
|
|
||||||
def delReplyto(self):
|
|
||||||
self.delAddresses('replyto')
|
|
||||||
|
|
||||||
def delTo(self):
|
|
||||||
self.delAddresses('to')
|
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
|
|
||||||
def getBcc(self):
|
|
||||||
return self.getAddresses('bcc')
|
|
||||||
|
|
||||||
def getCc(self):
|
|
||||||
return self.getAddresses('cc')
|
|
||||||
|
|
||||||
def getNoreply(self):
|
|
||||||
return self.getAddresses('noreply')
|
|
||||||
|
|
||||||
def getReplyroom(self):
|
|
||||||
return self.getAddresses('replyroom')
|
|
||||||
|
|
||||||
def getReplyto(self):
|
|
||||||
return self.getAddresses('replyto')
|
|
||||||
|
|
||||||
def getTo(self):
|
|
||||||
return self.getAddresses('to')
|
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
|
||||||
|
|
||||||
def setBcc(self, addresses):
|
|
||||||
self.setAddresses(addresses, 'bcc')
|
|
||||||
|
|
||||||
def setCc(self, addresses):
|
|
||||||
self.setAddresses(addresses, 'cc')
|
|
||||||
|
|
||||||
def setNoreply(self, addresses):
|
|
||||||
self.setAddresses(addresses, 'noreply')
|
|
||||||
|
|
||||||
def setReplyroom(self, addresses):
|
|
||||||
self.setAddresses(addresses, 'replyroom')
|
|
||||||
|
|
||||||
def setReplyto(self, addresses):
|
|
||||||
self.setAddresses(addresses, 'replyto')
|
|
||||||
|
|
||||||
def setTo(self, addresses):
|
|
||||||
self.setAddresses(addresses, 'to')
|
|
||||||
|
|
||||||
|
|
||||||
class Address(ElementBase):
|
|
||||||
namespace = 'http://jabber.org/protocol/address'
|
|
||||||
name = 'address'
|
|
||||||
plugin_attrib = 'address'
|
|
||||||
interfaces = set(('delivered', 'desc', 'jid', 'node', 'type', 'uri'))
|
|
||||||
address_types = set(('bcc', 'cc', 'noreply', 'replyroom', 'replyto', 'to'))
|
|
||||||
|
|
||||||
def getDelivered(self):
|
|
||||||
return self.xml.attrib.get('delivered', False)
|
|
||||||
|
|
||||||
def setDelivered(self, delivered):
|
|
||||||
if delivered:
|
|
||||||
self.xml.attrib['delivered'] = "true"
|
|
||||||
else:
|
|
||||||
del self['delivered']
|
|
||||||
|
|
||||||
def setUri(self, uri):
|
|
||||||
if uri:
|
|
||||||
del self['jid']
|
|
||||||
del self['node']
|
|
||||||
self.xml.attrib['uri'] = uri
|
|
||||||
elif 'uri' in self.xml.attrib:
|
|
||||||
del self.xml.attrib['uri']
|
|
||||||
|
|
||||||
|
|
||||||
class XEP_0033(BasePlugin):
|
|
||||||
|
|
||||||
"""
|
|
||||||
XEP-0033: Extended Stanza Addressing
|
|
||||||
"""
|
|
||||||
|
|
||||||
name = 'xep_0033'
|
|
||||||
description = 'XEP-0033: Extended Stanza Addressing'
|
|
||||||
dependencies = set(['xep_0033'])
|
|
||||||
|
|
||||||
def plugin_init(self):
|
|
||||||
self.xep = '0033'
|
|
||||||
|
|
||||||
register_stanza_plugin(Message, Addresses)
|
|
||||||
|
|
||||||
self.xmpp.plugin['xep_0030'].add_feature(Addresses.namespace)
|
|
||||||
|
|
||||||
|
|
||||||
xep_0033 = XEP_0033
|
|
||||||
register_plugin(XEP_0033)
|
|
||||||
20
sleekxmpp/plugins/xep_0033/__init__.py
Normal file
20
sleekxmpp/plugins/xep_0033/__init__.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.base import register_plugin
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.xep_0033 import stanza
|
||||||
|
from sleekxmpp.plugins.xep_0033.stanza import Addresses, Address
|
||||||
|
from sleekxmpp.plugins.xep_0033.addresses import XEP_0033
|
||||||
|
|
||||||
|
|
||||||
|
register_plugin(XEP_0033)
|
||||||
|
|
||||||
|
# Retain some backwards compatibility
|
||||||
|
xep_0033 = XEP_0033
|
||||||
|
Addresses.addAddress = Addresses.add_address
|
||||||
37
sleekxmpp/plugins/xep_0033/addresses.py
Normal file
37
sleekxmpp/plugins/xep_0033/addresses.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from sleekxmpp import Message, Presence
|
||||||
|
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||||
|
from sleekxmpp.plugins import BasePlugin
|
||||||
|
from sleekxmpp.plugins.xep_0033 import stanza, Addresses
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0033(BasePlugin):
|
||||||
|
|
||||||
|
"""
|
||||||
|
XEP-0033: Extended Stanza Addressing
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = 'xep_0033'
|
||||||
|
description = 'XEP-0033: Extended Stanza Addressing'
|
||||||
|
dependencies = set(['xep_0030'])
|
||||||
|
stanza = stanza
|
||||||
|
|
||||||
|
def plugin_init(self):
|
||||||
|
register_stanza_plugin(Message, Addresses)
|
||||||
|
register_stanza_plugin(Presence, Addresses)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=Addresses.namespace)
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
|
self.xmpp['xep_0030'].add_feature(Addresses.namespace)
|
||||||
|
|
||||||
131
sleekxmpp/plugins/xep_0033/stanza.py
Normal file
131
sleekxmpp/plugins/xep_0033/stanza.py
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.xmlstream import JID, ElementBase, ET, register_stanza_plugin
|
||||||
|
|
||||||
|
|
||||||
|
class Addresses(ElementBase):
|
||||||
|
|
||||||
|
name = 'addresses'
|
||||||
|
namespace = 'http://jabber.org/protocol/address'
|
||||||
|
plugin_attrib = 'addresses'
|
||||||
|
interfaces = set()
|
||||||
|
|
||||||
|
def add_address(self, atype='to', jid='', node='', uri='',
|
||||||
|
desc='', delivered=False):
|
||||||
|
addr = Address(parent=self)
|
||||||
|
addr['type'] = atype
|
||||||
|
addr['jid'] = jid
|
||||||
|
addr['node'] = node
|
||||||
|
addr['uri'] = uri
|
||||||
|
addr['desc'] = desc
|
||||||
|
addr['delivered'] = delivered
|
||||||
|
|
||||||
|
return addr
|
||||||
|
|
||||||
|
# Additional methods for manipulating sets of addresses
|
||||||
|
# based on type are generated below.
|
||||||
|
|
||||||
|
|
||||||
|
class Address(ElementBase):
|
||||||
|
|
||||||
|
name = 'address'
|
||||||
|
namespace = 'http://jabber.org/protocol/address'
|
||||||
|
plugin_attrib = 'address'
|
||||||
|
interfaces = set(['type', 'jid', 'node', 'uri', 'desc', 'delivered'])
|
||||||
|
|
||||||
|
address_types = set(('bcc', 'cc', 'noreply', 'replyroom', 'replyto', 'to'))
|
||||||
|
|
||||||
|
def get_jid(self):
|
||||||
|
return JID(self._get_attr('jid'))
|
||||||
|
|
||||||
|
def set_jid(self, value):
|
||||||
|
self._set_attr('jid', str(value))
|
||||||
|
|
||||||
|
def get_delivered(self):
|
||||||
|
value = self._get_attr('delivered', False)
|
||||||
|
return value and value.lower() in ('true', '1')
|
||||||
|
|
||||||
|
def set_delivered(self, delivered):
|
||||||
|
if delivered:
|
||||||
|
self._set_attr('delivered', 'true')
|
||||||
|
else:
|
||||||
|
del self['delivered']
|
||||||
|
|
||||||
|
def set_uri(self, uri):
|
||||||
|
if uri:
|
||||||
|
del self['jid']
|
||||||
|
del self['node']
|
||||||
|
self._set_attr('uri', uri)
|
||||||
|
else:
|
||||||
|
self._del_attr('uri')
|
||||||
|
|
||||||
|
|
||||||
|
# =====================================================================
|
||||||
|
# Auto-generate address type filters for the Addresses class.
|
||||||
|
|
||||||
|
def _addr_filter(atype):
|
||||||
|
def _type_filter(addr):
|
||||||
|
if isinstance(addr, Address):
|
||||||
|
if atype == 'all' or addr['type'] == atype:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
return _type_filter
|
||||||
|
|
||||||
|
|
||||||
|
def _build_methods(atype):
|
||||||
|
|
||||||
|
def get_multi(self):
|
||||||
|
return list(filter(_addr_filter(atype), self))
|
||||||
|
|
||||||
|
def set_multi(self, value):
|
||||||
|
del self[atype]
|
||||||
|
for addr in value:
|
||||||
|
|
||||||
|
# Support assigning dictionary versions of addresses
|
||||||
|
# instead of full Address objects.
|
||||||
|
if not isinstance(addr, Address):
|
||||||
|
if atype != 'all':
|
||||||
|
addr['type'] = atype
|
||||||
|
elif 'atype' in addr and 'type' not in addr:
|
||||||
|
addr['type'] = addr['atype']
|
||||||
|
addrObj = Address()
|
||||||
|
addrObj.values = addr
|
||||||
|
addr = addrObj
|
||||||
|
|
||||||
|
self.append(addr)
|
||||||
|
|
||||||
|
def del_multi(self):
|
||||||
|
res = list(filter(_addr_filter(atype), self))
|
||||||
|
for addr in res:
|
||||||
|
self.iterables.remove(addr)
|
||||||
|
self.xml.remove(addr.xml)
|
||||||
|
|
||||||
|
return get_multi, set_multi, del_multi
|
||||||
|
|
||||||
|
|
||||||
|
for atype in ('all', 'bcc', 'cc', 'noreply', 'replyroom', 'replyto', 'to'):
|
||||||
|
get_multi, set_multi, del_multi = _build_methods(atype)
|
||||||
|
|
||||||
|
Addresses.interfaces.add(atype)
|
||||||
|
setattr(Addresses, "get_%s" % atype, get_multi)
|
||||||
|
setattr(Addresses, "set_%s" % atype, set_multi)
|
||||||
|
setattr(Addresses, "del_%s" % atype, del_multi)
|
||||||
|
|
||||||
|
# To retain backwards compatibility:
|
||||||
|
setattr(Addresses, "get%s" % atype.title(), get_multi)
|
||||||
|
setattr(Addresses, "set%s" % atype.title(), set_multi)
|
||||||
|
setattr(Addresses, "del%s" % atype.title(), del_multi)
|
||||||
|
if atype == 'all':
|
||||||
|
Addresses.interfaces.add('addresses')
|
||||||
|
setattr(Addresses, "getAddresses", get_multi)
|
||||||
|
setattr(Addresses, "setAddresses", set_multi)
|
||||||
|
setattr(Addresses, "delAddresses", del_multi)
|
||||||
|
|
||||||
|
|
||||||
|
register_stanza_plugin(Addresses, Address, iterable=True)
|
||||||
@@ -51,6 +51,13 @@ class XEP_0047(BasePlugin):
|
|||||||
StanzaPath('iq@type=set/ibb_data'),
|
StanzaPath('iq@type=set/ibb_data'),
|
||||||
self._handle_data))
|
self._handle_data))
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp.remove_handler('IBB Open')
|
||||||
|
self.xmpp.remove_handler('IBB Close')
|
||||||
|
self.xmpp.remove_handler('IBB Data')
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature='http://jabber.org/protocol/ibb')
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp['xep_0030'].add_feature('http://jabber.org/protocol/ibb')
|
self.xmpp['xep_0030'].add_feature('http://jabber.org/protocol/ibb')
|
||||||
|
|
||||||
def _accept_stream(self, iq):
|
def _accept_stream(self, iq):
|
||||||
|
|||||||
@@ -110,6 +110,20 @@ class XEP_0050(BasePlugin):
|
|||||||
self._handle_command_complete,
|
self._handle_command_complete,
|
||||||
threaded=self.threaded)
|
threaded=self.threaded)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp.del_event_handler('command_execute',
|
||||||
|
self._handle_command_start)
|
||||||
|
self.xmpp.del_event_handler('command_next',
|
||||||
|
self._handle_command_next)
|
||||||
|
self.xmpp.del_event_handler('command_cancel',
|
||||||
|
self._handle_command_cancel)
|
||||||
|
self.xmpp.del_event_handler('command_complete',
|
||||||
|
self._handle_command_complete)
|
||||||
|
self.xmpp.remove_handler('Ad-Hoc Execute')
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=Command.namespace)
|
||||||
|
self.xmpp['xep_0030'].set_items(node=Command.namespace, items=tuple())
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp['xep_0030'].add_feature(Command.namespace)
|
self.xmpp['xep_0030'].add_feature(Command.namespace)
|
||||||
self.xmpp['xep_0030'].set_items(node=Command.namespace, items=tuple())
|
self.xmpp['xep_0030'].set_items(node=Command.namespace, items=tuple())
|
||||||
|
|
||||||
|
|||||||
@@ -110,14 +110,14 @@ class Command(ElementBase):
|
|||||||
"""
|
"""
|
||||||
Return the set of allowable next actions.
|
Return the set of allowable next actions.
|
||||||
"""
|
"""
|
||||||
actions = []
|
actions = set()
|
||||||
actions_xml = self.find('{%s}actions' % self.namespace)
|
actions_xml = self.find('{%s}actions' % self.namespace)
|
||||||
if actions_xml is not None:
|
if actions_xml is not None:
|
||||||
for action in self.next_actions:
|
for action in self.next_actions:
|
||||||
action_xml = actions_xml.find('{%s}%s' % (self.namespace,
|
action_xml = actions_xml.find('{%s}%s' % (self.namespace,
|
||||||
action))
|
action))
|
||||||
if action_xml is not None:
|
if action_xml is not None:
|
||||||
actions.append(action)
|
actions.add(action)
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
def del_actions(self):
|
def del_actions(self):
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ class Nickname(ElementBase):
|
|||||||
name = 'NICKNAME'
|
name = 'NICKNAME'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'nicknames'
|
||||||
interfaces = set([name])
|
interfaces = set([name])
|
||||||
is_extension = True
|
is_extension = True
|
||||||
|
|
||||||
@@ -94,6 +95,7 @@ class Email(ElementBase):
|
|||||||
name = 'EMAIL'
|
name = 'EMAIL'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'emails'
|
||||||
interfaces = set(['HOME', 'WORK', 'INTERNET', 'PREF', 'X400', 'USERID'])
|
interfaces = set(['HOME', 'WORK', 'INTERNET', 'PREF', 'X400', 'USERID'])
|
||||||
sub_interfaces = set(['USERID'])
|
sub_interfaces = set(['USERID'])
|
||||||
bool_interfaces = set(['HOME', 'WORK', 'INTERNET', 'PREF', 'X400'])
|
bool_interfaces = set(['HOME', 'WORK', 'INTERNET', 'PREF', 'X400'])
|
||||||
@@ -103,6 +105,7 @@ class Address(ElementBase):
|
|||||||
name = 'ADR'
|
name = 'ADR'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'addresses'
|
||||||
interfaces = set(['HOME', 'WORK', 'POSTAL', 'PARCEL', 'DOM', 'INTL',
|
interfaces = set(['HOME', 'WORK', 'POSTAL', 'PARCEL', 'DOM', 'INTL',
|
||||||
'PREF', 'POBOX', 'EXTADD', 'STREET', 'LOCALITY',
|
'PREF', 'POBOX', 'EXTADD', 'STREET', 'LOCALITY',
|
||||||
'REGION', 'PCODE', 'CTRY'])
|
'REGION', 'PCODE', 'CTRY'])
|
||||||
@@ -115,6 +118,7 @@ class Telephone(ElementBase):
|
|||||||
name = 'TEL'
|
name = 'TEL'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'telephone_numbers'
|
||||||
interfaces = set(['HOME', 'WORK', 'VOICE', 'FAX', 'PAGER', 'MSG',
|
interfaces = set(['HOME', 'WORK', 'VOICE', 'FAX', 'PAGER', 'MSG',
|
||||||
'CELL', 'VIDEO', 'BBS', 'MODEM', 'ISDN', 'PCS',
|
'CELL', 'VIDEO', 'BBS', 'MODEM', 'ISDN', 'PCS',
|
||||||
'PREF', 'NUMBER'])
|
'PREF', 'NUMBER'])
|
||||||
@@ -138,6 +142,7 @@ class Label(ElementBase):
|
|||||||
name = 'LABEL'
|
name = 'LABEL'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'labels'
|
||||||
interfaces = set(['HOME', 'WORK', 'POSTAL', 'PARCEL', 'DOM', 'INT',
|
interfaces = set(['HOME', 'WORK', 'POSTAL', 'PARCEL', 'DOM', 'INT',
|
||||||
'PREF', 'lines'])
|
'PREF', 'lines'])
|
||||||
bool_interfaces = set(['HOME', 'WORK', 'POSTAL', 'PARCEL', 'DOM',
|
bool_interfaces = set(['HOME', 'WORK', 'POSTAL', 'PARCEL', 'DOM',
|
||||||
@@ -171,6 +176,7 @@ class Geo(ElementBase):
|
|||||||
name = 'GEO'
|
name = 'GEO'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'geolocations'
|
||||||
interfaces = set(['LAT', 'LON'])
|
interfaces = set(['LAT', 'LON'])
|
||||||
sub_interfaces = interfaces
|
sub_interfaces = interfaces
|
||||||
|
|
||||||
@@ -179,6 +185,7 @@ class Org(ElementBase):
|
|||||||
name = 'ORG'
|
name = 'ORG'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'organizations'
|
||||||
interfaces = set(['ORGNAME', 'ORGUNIT', 'orgunits'])
|
interfaces = set(['ORGNAME', 'ORGUNIT', 'orgunits'])
|
||||||
sub_interfaces = set(['ORGNAME', 'ORGUNIT'])
|
sub_interfaces = set(['ORGNAME', 'ORGUNIT'])
|
||||||
|
|
||||||
@@ -210,6 +217,7 @@ class Photo(ElementBase):
|
|||||||
name = 'PHOTO'
|
name = 'PHOTO'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'photos'
|
||||||
interfaces = set(['TYPE', 'EXTVAL'])
|
interfaces = set(['TYPE', 'EXTVAL'])
|
||||||
sub_interfaces = interfaces
|
sub_interfaces = interfaces
|
||||||
|
|
||||||
@@ -218,14 +226,16 @@ class Logo(ElementBase):
|
|||||||
name = 'LOGO'
|
name = 'LOGO'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'logos'
|
||||||
interfaces = set(['TYPE', 'EXTVAL'])
|
interfaces = set(['TYPE', 'EXTVAL'])
|
||||||
sub_interfaces = interfaces
|
sub_interfaces = interfaces
|
||||||
|
|
||||||
|
|
||||||
class Sound(ElementBase):
|
class Sound(ElementBase):
|
||||||
name = 'LOGO'
|
name = 'SOUND'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'sounds'
|
||||||
interfaces = set(['PHONETC', 'EXTVAL'])
|
interfaces = set(['PHONETC', 'EXTVAL'])
|
||||||
sub_interfaces = interfaces
|
sub_interfaces = interfaces
|
||||||
|
|
||||||
@@ -264,6 +274,7 @@ class Classification(ElementBase):
|
|||||||
name = 'CLASS'
|
name = 'CLASS'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'classifications'
|
||||||
interfaces = set(['PUBLIC', 'PRIVATE', 'CONFIDENTIAL'])
|
interfaces = set(['PUBLIC', 'PRIVATE', 'CONFIDENTIAL'])
|
||||||
bool_interfaces = interfaces
|
bool_interfaces = interfaces
|
||||||
|
|
||||||
@@ -272,6 +283,7 @@ class Categories(ElementBase):
|
|||||||
name = 'CATEGORIES'
|
name = 'CATEGORIES'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'categories'
|
||||||
interfaces = set([name])
|
interfaces = set([name])
|
||||||
is_extension = True
|
is_extension = True
|
||||||
|
|
||||||
@@ -301,6 +313,7 @@ class Birthday(ElementBase):
|
|||||||
name = 'BDAY'
|
name = 'BDAY'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'birthdays'
|
||||||
interfaces = set([name])
|
interfaces = set([name])
|
||||||
is_extension = True
|
is_extension = True
|
||||||
|
|
||||||
@@ -319,6 +332,7 @@ class Rev(ElementBase):
|
|||||||
name = 'REV'
|
name = 'REV'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'revision_dates'
|
||||||
interfaces = set([name])
|
interfaces = set([name])
|
||||||
is_extension = True
|
is_extension = True
|
||||||
|
|
||||||
@@ -337,6 +351,7 @@ class Title(ElementBase):
|
|||||||
name = 'TITLE'
|
name = 'TITLE'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'titles'
|
||||||
interfaces = set([name])
|
interfaces = set([name])
|
||||||
is_extension = True
|
is_extension = True
|
||||||
|
|
||||||
@@ -351,6 +366,7 @@ class Role(ElementBase):
|
|||||||
name = 'ROLE'
|
name = 'ROLE'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'roles'
|
||||||
interfaces = set([name])
|
interfaces = set([name])
|
||||||
is_extension = True
|
is_extension = True
|
||||||
|
|
||||||
@@ -365,6 +381,7 @@ class Note(ElementBase):
|
|||||||
name = 'NOTE'
|
name = 'NOTE'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'notes'
|
||||||
interfaces = set([name])
|
interfaces = set([name])
|
||||||
is_extension = True
|
is_extension = True
|
||||||
|
|
||||||
@@ -379,6 +396,7 @@ class Desc(ElementBase):
|
|||||||
name = 'DESC'
|
name = 'DESC'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'descriptions'
|
||||||
interfaces = set([name])
|
interfaces = set([name])
|
||||||
is_extension = True
|
is_extension = True
|
||||||
|
|
||||||
@@ -393,6 +411,7 @@ class URL(ElementBase):
|
|||||||
name = 'URL'
|
name = 'URL'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'urls'
|
||||||
interfaces = set([name])
|
interfaces = set([name])
|
||||||
is_extension = True
|
is_extension = True
|
||||||
|
|
||||||
@@ -407,6 +426,7 @@ class UID(ElementBase):
|
|||||||
name = 'UID'
|
name = 'UID'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'uids'
|
||||||
interfaces = set([name])
|
interfaces = set([name])
|
||||||
is_extension = True
|
is_extension = True
|
||||||
|
|
||||||
@@ -421,6 +441,7 @@ class ProdID(ElementBase):
|
|||||||
name = 'PRODID'
|
name = 'PRODID'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'product_ids'
|
||||||
interfaces = set([name])
|
interfaces = set([name])
|
||||||
is_extension = True
|
is_extension = True
|
||||||
|
|
||||||
@@ -435,6 +456,7 @@ class Mailer(ElementBase):
|
|||||||
name = 'MAILER'
|
name = 'MAILER'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'mailers'
|
||||||
interfaces = set([name])
|
interfaces = set([name])
|
||||||
is_extension = True
|
is_extension = True
|
||||||
|
|
||||||
@@ -449,6 +471,7 @@ class SortString(ElementBase):
|
|||||||
name = 'SORT-STRING'
|
name = 'SORT-STRING'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = 'SORT_STRING'
|
plugin_attrib = 'SORT_STRING'
|
||||||
|
plugin_multi_attrib = 'sort_strings'
|
||||||
interfaces = set([name])
|
interfaces = set([name])
|
||||||
is_extension = True
|
is_extension = True
|
||||||
|
|
||||||
@@ -463,6 +486,7 @@ class Agent(ElementBase):
|
|||||||
name = 'AGENT'
|
name = 'AGENT'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'agents'
|
||||||
interfaces = set(['EXTVAL'])
|
interfaces = set(['EXTVAL'])
|
||||||
sub_interfaces = interfaces
|
sub_interfaces = interfaces
|
||||||
|
|
||||||
@@ -471,6 +495,7 @@ class JabberID(ElementBase):
|
|||||||
name = 'JABBERID'
|
name = 'JABBERID'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'jids'
|
||||||
interfaces = set([name])
|
interfaces = set([name])
|
||||||
is_extension = True
|
is_extension = True
|
||||||
|
|
||||||
@@ -485,6 +510,7 @@ class TimeZone(ElementBase):
|
|||||||
name = 'TZ'
|
name = 'TZ'
|
||||||
namespace = 'vcard-temp'
|
namespace = 'vcard-temp'
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
plugin_multi_attrib = 'timezones'
|
||||||
interfaces = set([name])
|
interfaces = set([name])
|
||||||
is_extension = True
|
is_extension = True
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ class XEP_0054(BasePlugin):
|
|||||||
"""
|
"""
|
||||||
register_stanza_plugin(Iq, VCardTemp)
|
register_stanza_plugin(Iq, VCardTemp)
|
||||||
|
|
||||||
self.xmpp['xep_0030'].add_feature('vcard-temp')
|
|
||||||
|
|
||||||
self.api.register(self._set_vcard, 'set_vcard', default=True)
|
self.api.register(self._set_vcard, 'set_vcard', default=True)
|
||||||
self.api.register(self._get_vcard, 'get_vcard', default=True)
|
self.api.register(self._get_vcard, 'get_vcard', default=True)
|
||||||
@@ -50,6 +49,13 @@ class XEP_0054(BasePlugin):
|
|||||||
StanzaPath('iq/vcard_temp'),
|
StanzaPath('iq/vcard_temp'),
|
||||||
self._handle_get_vcard))
|
self._handle_get_vcard))
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp.remove_handler('VCardTemp')
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature='vcard-temp')
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
|
self.xmpp['xep_0030'].add_feature('vcard-temp')
|
||||||
|
|
||||||
def make_vcard(self):
|
def make_vcard(self):
|
||||||
return VCardTemp()
|
return VCardTemp()
|
||||||
|
|
||||||
@@ -107,7 +113,7 @@ class XEP_0054(BasePlugin):
|
|||||||
self.api['set_vcard'](jid=iq['from'], args=iq['vcard_temp'])
|
self.api['set_vcard'](jid=iq['from'], args=iq['vcard_temp'])
|
||||||
return
|
return
|
||||||
elif iq['type'] == 'get':
|
elif iq['type'] == 'get':
|
||||||
vcard = self.api['get_vard'](iq['from'].bare)
|
vcard = self.api['get_vcard'](iq['from'].bare)
|
||||||
if isinstance(vcard, Iq):
|
if isinstance(vcard, Iq):
|
||||||
vcard.send()
|
vcard.send()
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ class ResultIterator():
|
|||||||
self.start = start
|
self.start = start
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
self.reverse = reverse
|
self.reverse = reverse
|
||||||
|
self._stop = False
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self
|
return self
|
||||||
@@ -62,6 +63,8 @@ class ResultIterator():
|
|||||||
results will be the items before the current page
|
results will be the items before the current page
|
||||||
of items.
|
of items.
|
||||||
"""
|
"""
|
||||||
|
if self._stop:
|
||||||
|
raise StopIteration
|
||||||
self.query[self.interface]['rsm']['before'] = self.reverse
|
self.query[self.interface]['rsm']['before'] = self.reverse
|
||||||
self.query['id'] = self.query.stream.new_id()
|
self.query['id'] = self.query.stream.new_id()
|
||||||
self.query[self.interface]['rsm']['max'] = str(self.amount)
|
self.query[self.interface]['rsm']['max'] = str(self.amount)
|
||||||
@@ -84,7 +87,7 @@ class ResultIterator():
|
|||||||
first = int(r[self.interface]['rsm']['first_index'])
|
first = int(r[self.interface]['rsm']['first_index'])
|
||||||
num_items = len(r[self.interface]['substanzas'])
|
num_items = len(r[self.interface]['substanzas'])
|
||||||
if first + num_items == count:
|
if first + num_items == count:
|
||||||
raise StopIteration
|
self._stop = True
|
||||||
|
|
||||||
if self.reverse:
|
if self.reverse:
|
||||||
self.start = r[self.interface]['rsm']['first']
|
self.start = r[self.interface]['rsm']['first']
|
||||||
@@ -111,10 +114,15 @@ class XEP_0059(BasePlugin):
|
|||||||
"""
|
"""
|
||||||
Start the XEP-0059 plugin.
|
Start the XEP-0059 plugin.
|
||||||
"""
|
"""
|
||||||
self.xmpp['xep_0030'].add_feature(Set.namespace)
|
|
||||||
register_stanza_plugin(self.xmpp['xep_0030'].stanza.DiscoItems,
|
register_stanza_plugin(self.xmpp['xep_0030'].stanza.DiscoItems,
|
||||||
self.stanza.Set)
|
self.stanza.Set)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=Set.namespace)
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
|
self.xmpp['xep_0030'].add_feature(Set.namespace)
|
||||||
|
|
||||||
def iterate(self, stanza, interface):
|
def iterate(self, stanza, interface):
|
||||||
"""
|
"""
|
||||||
Create a new result set iterator for a given stanza query.
|
Create a new result set iterator for a given stanza query.
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class Set(ElementBase):
|
|||||||
if fi is not None:
|
if fi is not None:
|
||||||
if val:
|
if val:
|
||||||
fi.attrib['index'] = val
|
fi.attrib['index'] = val
|
||||||
else:
|
elif 'index' in fi.attrib:
|
||||||
del fi.attrib['index']
|
del fi.attrib['index']
|
||||||
elif val:
|
elif val:
|
||||||
fi = ET.Element("{%s}first" % (self.namespace))
|
fi = ET.Element("{%s}first" % (self.namespace))
|
||||||
@@ -93,7 +93,7 @@ class Set(ElementBase):
|
|||||||
|
|
||||||
def set_before(self, val):
|
def set_before(self, val):
|
||||||
b = self.xml.find("{%s}before" % (self.namespace))
|
b = self.xml.find("{%s}before" % (self.namespace))
|
||||||
if b is None and val == True:
|
if b is None and val is True:
|
||||||
self._set_sub_text('{%s}before' % self.namespace, '', True)
|
self._set_sub_text('{%s}before' % self.namespace, '', True)
|
||||||
else:
|
else:
|
||||||
self._set_sub_text('{%s}before' % self.namespace, val)
|
self._set_sub_text('{%s}before' % self.namespace, val)
|
||||||
|
|||||||
@@ -53,6 +53,13 @@ class XEP_0060(BasePlugin):
|
|||||||
StanzaPath('message/pubsub_event/subscription'),
|
StanzaPath('message/pubsub_event/subscription'),
|
||||||
self._handle_event_subscription))
|
self._handle_event_subscription))
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp.remove_handler('Pubsub Event: Items')
|
||||||
|
self.xmpp.remove_handler('Pubsub Event: Purge')
|
||||||
|
self.xmpp.remove_handler('Pubsub Event: Delete')
|
||||||
|
self.xmpp.remove_handler('Pubsub Event: Configuration')
|
||||||
|
self.xmpp.remove_handler('Pubsub Event: Subscription')
|
||||||
|
|
||||||
def _handle_event_items(self, msg):
|
def _handle_event_items(self, msg):
|
||||||
"""Raise events for publish and retraction notifications."""
|
"""Raise events for publish and retraction notifications."""
|
||||||
node = msg['pubsub_event']['items']['node']
|
node = msg['pubsub_event']['items']['node']
|
||||||
|
|||||||
@@ -77,12 +77,12 @@ class Item(ElementBase):
|
|||||||
self.append(value)
|
self.append(value)
|
||||||
|
|
||||||
def get_payload(self):
|
def get_payload(self):
|
||||||
childs = self.xml.getchildren()
|
childs = list(self.xml)
|
||||||
if len(childs) > 0:
|
if len(childs) > 0:
|
||||||
return childs[0]
|
return childs[0]
|
||||||
|
|
||||||
def del_payload(self):
|
def del_payload(self):
|
||||||
for child in self.xml.getchildren():
|
for child in self.xml:
|
||||||
self.xml.remove(child)
|
self.xml.remove(child)
|
||||||
|
|
||||||
|
|
||||||
@@ -254,12 +254,12 @@ class PubsubState(ElementBase):
|
|||||||
self.xml.append(value)
|
self.xml.append(value)
|
||||||
|
|
||||||
def get_payload(self):
|
def get_payload(self):
|
||||||
childs = self.xml.getchildren()
|
childs = list(self.xml)
|
||||||
if len(childs) > 0:
|
if len(childs) > 0:
|
||||||
return childs[0]
|
return childs[0]
|
||||||
|
|
||||||
def del_payload(self):
|
def del_payload(self):
|
||||||
for child in self.xml.getchildren():
|
for child in self.xml:
|
||||||
self.xml.remove(child)
|
self.xml.remove(child)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class PubsubErrorCondition(ElementBase):
|
|||||||
|
|
||||||
def get_condition(self):
|
def get_condition(self):
|
||||||
"""Return the condition element's name."""
|
"""Return the condition element's name."""
|
||||||
for child in self.parent().xml.getchildren():
|
for child in self.parent().xml:
|
||||||
if "{%s}" % self.condition_ns in child.tag:
|
if "{%s}" % self.condition_ns in child.tag:
|
||||||
cond = child.tag.split('}', 1)[-1]
|
cond = child.tag.split('}', 1)[-1]
|
||||||
if cond in self.conditions:
|
if cond in self.conditions:
|
||||||
@@ -55,7 +55,7 @@ class PubsubErrorCondition(ElementBase):
|
|||||||
|
|
||||||
def del_condition(self):
|
def del_condition(self):
|
||||||
"""Remove the condition element."""
|
"""Remove the condition element."""
|
||||||
for child in self.parent().xml.getchildren():
|
for child in self.parent().xml:
|
||||||
if "{%s}" % self.condition_ns in child.tag:
|
if "{%s}" % self.condition_ns in child.tag:
|
||||||
tag = child.tag.split('}', 1)[-1]
|
tag = child.tag.split('}', 1)[-1]
|
||||||
if tag in self.conditions:
|
if tag in self.conditions:
|
||||||
|
|||||||
@@ -31,12 +31,12 @@ class EventItem(ElementBase):
|
|||||||
self.xml.append(value)
|
self.xml.append(value)
|
||||||
|
|
||||||
def get_payload(self):
|
def get_payload(self):
|
||||||
childs = self.xml.getchildren()
|
childs = list(self.xml)
|
||||||
if len(childs) > 0:
|
if len(childs) > 0:
|
||||||
return childs[0]
|
return childs[0]
|
||||||
|
|
||||||
def del_payload(self):
|
def del_payload(self):
|
||||||
for child in self.xml.getchildren():
|
for child in self.xml:
|
||||||
self.xml.remove(child)
|
self.xml.remove(child)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,12 @@ class XEP_0066(BasePlugin):
|
|||||||
StanzaPath('iq@type=set/oob_transfer'),
|
StanzaPath('iq@type=set/oob_transfer'),
|
||||||
self._handle_transfer))
|
self._handle_transfer))
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp.remove_handler('OOB Transfer')
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=stanza.OOBTransfer.namespace)
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=stanza.OOB.namespace)
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp['xep_0030'].add_feature(stanza.OOBTransfer.namespace)
|
self.xmpp['xep_0030'].add_feature(stanza.OOBTransfer.namespace)
|
||||||
self.xmpp['xep_0030'].add_feature(stanza.OOB.namespace)
|
self.xmpp['xep_0030'].add_feature(stanza.OOB.namespace)
|
||||||
|
|
||||||
|
|||||||
@@ -34,9 +34,7 @@ class XEP_0077(BasePlugin):
|
|||||||
register_stanza_plugin(StreamFeatures, RegisterFeature)
|
register_stanza_plugin(StreamFeatures, RegisterFeature)
|
||||||
register_stanza_plugin(Iq, Register)
|
register_stanza_plugin(Iq, Register)
|
||||||
|
|
||||||
if self.xmpp.is_component:
|
if not self.xmpp.is_component:
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.xmpp.register_feature('register',
|
self.xmpp.register_feature('register',
|
||||||
self._handle_register_feature,
|
self._handle_register_feature,
|
||||||
restart=False,
|
restart=False,
|
||||||
@@ -45,6 +43,10 @@ class XEP_0077(BasePlugin):
|
|||||||
register_stanza_plugin(Register, self.xmpp['xep_0004'].stanza.Form)
|
register_stanza_plugin(Register, self.xmpp['xep_0004'].stanza.Form)
|
||||||
register_stanza_plugin(Register, self.xmpp['xep_0066'].stanza.OOB)
|
register_stanza_plugin(Register, self.xmpp['xep_0066'].stanza.OOB)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
if not self.xmpp.is_component:
|
||||||
|
self.xmpp.unregister_feature('register', self.config.get('order', 50))
|
||||||
|
|
||||||
def _handle_register_feature(self, features):
|
def _handle_register_feature(self, features):
|
||||||
if 'mechanisms' in self.xmpp.features:
|
if 'mechanisms' in self.xmpp.features:
|
||||||
# We have already logged in with an account
|
# We have already logged in with an account
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ class XEP_0078(BasePlugin):
|
|||||||
register_stanza_plugin(Iq, stanza.IqAuth)
|
register_stanza_plugin(Iq, stanza.IqAuth)
|
||||||
register_stanza_plugin(StreamFeatures, stanza.AuthFeature)
|
register_stanza_plugin(StreamFeatures, stanza.AuthFeature)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp.unregister_feature('auth', self.config.get('order', 15))
|
||||||
|
|
||||||
def _handle_auth(self, features):
|
def _handle_auth(self, features):
|
||||||
# If we can or have already authenticated with SASL, do nothing.
|
# If we can or have already authenticated with SASL, do nothing.
|
||||||
if 'mechanisms' in features['features']:
|
if 'mechanisms' in features['features']:
|
||||||
|
|||||||
@@ -39,5 +39,3 @@ class AuthFeature(ElementBase):
|
|||||||
interfaces = set()
|
interfaces = set()
|
||||||
plugin_tag_map = {}
|
plugin_tag_map = {}
|
||||||
plugin_attrib_map = {}
|
plugin_attrib_map = {}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,11 @@ class XEP_0080(BasePlugin):
|
|||||||
dependencies = set(['xep_0163'])
|
dependencies = set(['xep_0163'])
|
||||||
stanza = stanza
|
stanza = stanza
|
||||||
|
|
||||||
def plugin_init(self):
|
def plugin_end(self):
|
||||||
"""Start the XEP-0080 plugin."""
|
self.xmpp['xep_0163'].remove_interest(Geoloc.namespace)
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=Geoloc.namespace)
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp['xep_0163'].register_pep('user_location', Geoloc)
|
self.xmpp['xep_0163'].register_pep('user_location', Geoloc)
|
||||||
|
|
||||||
def publish_location(self, **kwargs):
|
def publish_location(self, **kwargs):
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ def format_date(time_obj):
|
|||||||
time_obj = time_obj.date()
|
time_obj = time_obj.date()
|
||||||
return time_obj.isoformat()
|
return time_obj.isoformat()
|
||||||
|
|
||||||
|
|
||||||
def format_time(time_obj):
|
def format_time(time_obj):
|
||||||
"""
|
"""
|
||||||
Return a formatted string version of a time object.
|
Return a formatted string version of a time object.
|
||||||
@@ -60,6 +61,7 @@ def format_time(time_obj):
|
|||||||
return '%sZ' % timestamp
|
return '%sZ' % timestamp
|
||||||
return timestamp
|
return timestamp
|
||||||
|
|
||||||
|
|
||||||
def format_datetime(time_obj):
|
def format_datetime(time_obj):
|
||||||
"""
|
"""
|
||||||
Return a formatted string version of a datetime object.
|
Return a formatted string version of a datetime object.
|
||||||
@@ -76,6 +78,7 @@ def format_datetime(time_obj):
|
|||||||
return '%sZ' % timestamp
|
return '%sZ' % timestamp
|
||||||
return timestamp
|
return timestamp
|
||||||
|
|
||||||
|
|
||||||
def date(year=None, month=None, day=None, obj=False):
|
def date(year=None, month=None, day=None, obj=False):
|
||||||
"""
|
"""
|
||||||
Create a date only timestamp for the given instant.
|
Create a date only timestamp for the given instant.
|
||||||
@@ -101,6 +104,7 @@ def date(year=None, month=None, day=None, obj=False):
|
|||||||
return value
|
return value
|
||||||
return format_date(value)
|
return format_date(value)
|
||||||
|
|
||||||
|
|
||||||
def time(hour=None, min=None, sec=None, micro=None, offset=None, obj=False):
|
def time(hour=None, min=None, sec=None, micro=None, offset=None, obj=False):
|
||||||
"""
|
"""
|
||||||
Create a time only timestamp for the given instant.
|
Create a time only timestamp for the given instant.
|
||||||
@@ -136,6 +140,7 @@ def time(hour=None, min=None, sec=None, micro=None, offset=None, obj=False):
|
|||||||
return value
|
return value
|
||||||
return format_time(value)
|
return format_time(value)
|
||||||
|
|
||||||
|
|
||||||
def datetime(year=None, month=None, day=None, hour=None,
|
def datetime(year=None, month=None, day=None, hour=None,
|
||||||
min=None, sec=None, micro=None, offset=None,
|
min=None, sec=None, micro=None, offset=None,
|
||||||
separators=True, obj=False):
|
separators=True, obj=False):
|
||||||
|
|||||||
16
sleekxmpp/plugins/xep_0084/__init__.py
Normal file
16
sleekxmpp/plugins/xep_0084/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.base import register_plugin
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.xep_0084 import stanza
|
||||||
|
from sleekxmpp.plugins.xep_0084.stanza import Data, MetaData
|
||||||
|
from sleekxmpp.plugins.xep_0084.avatar import XEP_0084
|
||||||
|
|
||||||
|
|
||||||
|
register_plugin(XEP_0084)
|
||||||
106
sleekxmpp/plugins/xep_0084/avatar.py
Normal file
106
sleekxmpp/plugins/xep_0084/avatar.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from sleekxmpp import Iq
|
||||||
|
from sleekxmpp.plugins import BasePlugin
|
||||||
|
from sleekxmpp.xmlstream.handler import Callback
|
||||||
|
from sleekxmpp.xmlstream.matcher import StanzaPath
|
||||||
|
from sleekxmpp.xmlstream import register_stanza_plugin, JID
|
||||||
|
from sleekxmpp.plugins.xep_0084 import stanza, Data, MetaData
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0084(BasePlugin):
|
||||||
|
|
||||||
|
name = 'xep_0084'
|
||||||
|
description = 'XEP-0084: User Avatar'
|
||||||
|
dependencies = set(['xep_0163', 'xep_0060'])
|
||||||
|
stanza = stanza
|
||||||
|
|
||||||
|
def plugin_init(self):
|
||||||
|
pubsub_stanza = self.xmpp['xep_0060'].stanza
|
||||||
|
register_stanza_plugin(pubsub_stanza.Item, Data)
|
||||||
|
register_stanza_plugin(pubsub_stanza.EventItem, Data)
|
||||||
|
|
||||||
|
self.xmpp['xep_0060'].map_node_event(Data.namespace, 'avatar_data')
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=MetaData.namespace)
|
||||||
|
self.xmpp['xep_0163'].remove_interest(MetaData.namespace)
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
|
self.xmpp['xep_0163'].register_pep('avatar_metadata', MetaData)
|
||||||
|
|
||||||
|
def retrieve_avatar(self, jid, id, url=None, ifrom=None, block=True,
|
||||||
|
callback=None, timeout=None):
|
||||||
|
return self.xmpp['xep_0060'].get_item(jid, Data.namespace, id,
|
||||||
|
ifrom=ifrom,
|
||||||
|
block=block,
|
||||||
|
callback=callback,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
def publish_avatar(self, data, ifrom=None, block=True, callback=None,
|
||||||
|
timeout=None):
|
||||||
|
payload = Data()
|
||||||
|
payload['value'] = data
|
||||||
|
return self.xmpp['xep_0163'].publish(payload,
|
||||||
|
node=Data.namespace,
|
||||||
|
id=hashlib.sha1(data).hexdigest(),
|
||||||
|
ifrom=ifrom,
|
||||||
|
block=block,
|
||||||
|
callback=callback,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
def publish_avatar_metadata(self, items=None, pointers=None,
|
||||||
|
ifrom=None, block=True,
|
||||||
|
callback=None, timeout=None):
|
||||||
|
metadata = MetaData()
|
||||||
|
if items is None:
|
||||||
|
items = []
|
||||||
|
for info in items:
|
||||||
|
metadata.add_info(info['id'], info['type'], info['bytes'],
|
||||||
|
height=info.get('height', ''),
|
||||||
|
width=info.get('width', ''),
|
||||||
|
url=info.get('url', ''))
|
||||||
|
for pointer in pointers:
|
||||||
|
metadata.add_pointer(pointer)
|
||||||
|
|
||||||
|
return self.xmpp['xep_0163'].publish(payload,
|
||||||
|
node=Data.namespace,
|
||||||
|
id=hashlib.sha1(data).hexdigest(),
|
||||||
|
ifrom=ifrom,
|
||||||
|
block=block,
|
||||||
|
callback=callback,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
def stop(self, ifrom=None, block=True, callback=None, timeout=None):
|
||||||
|
"""
|
||||||
|
Clear existing avatar metadata information to stop notifications.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
ifrom -- Specify the sender's JID.
|
||||||
|
block -- Specify if the send call will block until a response
|
||||||
|
is received, or a timeout occurs. Defaults to True.
|
||||||
|
timeout -- The length of time (in seconds) to wait for a response
|
||||||
|
before exiting the send call if blocking is used.
|
||||||
|
Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
|
||||||
|
callback -- Optional reference to a stream handler function. Will
|
||||||
|
be executed when a reply stanza is received.
|
||||||
|
"""
|
||||||
|
metadata = MetaData()
|
||||||
|
return self.xmpp['xep_0163'].publish(metadata,
|
||||||
|
node=MetaData.namespace,
|
||||||
|
ifrom=ifrom,
|
||||||
|
block=block,
|
||||||
|
callback=callback,
|
||||||
|
timeout=timeout)
|
||||||
78
sleekxmpp/plugins/xep_0084/stanza.py
Normal file
78
sleekxmpp/plugins/xep_0084/stanza.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from base64 import b64encode, b64decode
|
||||||
|
from sleekxmpp.thirdparty.suelta.util import bytes
|
||||||
|
|
||||||
|
from sleekxmpp.xmlstream import ET, ElementBase, register_stanza_plugin
|
||||||
|
|
||||||
|
|
||||||
|
class Data(ElementBase):
|
||||||
|
name = 'data'
|
||||||
|
namespace = 'urn:xmpp:avatar:data'
|
||||||
|
plugin_attrib = 'avatar_data'
|
||||||
|
interfaces = set(['value'])
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
if self.xml.text:
|
||||||
|
return b64decode(bytes(self.xml.text))
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def set_value(self, value):
|
||||||
|
if value:
|
||||||
|
self.xml.text = b64encode(bytes(value))
|
||||||
|
else:
|
||||||
|
self.xml.text = ''
|
||||||
|
|
||||||
|
def del_value(self):
|
||||||
|
self.xml.text = ''
|
||||||
|
|
||||||
|
|
||||||
|
class MetaData(ElementBase):
|
||||||
|
name = 'metadata'
|
||||||
|
namespace = 'urn:xmpp:avatar:metadata'
|
||||||
|
plugin_attrib = 'avatar_metadata'
|
||||||
|
interfaces = set()
|
||||||
|
|
||||||
|
def add_info(self, id, itype, ibytes, height=None, width=None, url=None):
|
||||||
|
info = Info()
|
||||||
|
info.values = {'id': id,
|
||||||
|
'type': itype,
|
||||||
|
'bytes': ibytes,
|
||||||
|
'height': height,
|
||||||
|
'width': width,
|
||||||
|
'url': url}
|
||||||
|
self.append(info)
|
||||||
|
|
||||||
|
def add_pointer(self, xml):
|
||||||
|
if not isinstance(xml, Pointer):
|
||||||
|
pointer = Pointer()
|
||||||
|
pointer.append(xml)
|
||||||
|
self.append(pointer)
|
||||||
|
else:
|
||||||
|
self.append(xml)
|
||||||
|
|
||||||
|
|
||||||
|
class Info(ElementBase):
|
||||||
|
name = 'info'
|
||||||
|
namespace = 'urn:xmpp:avatar:metadata'
|
||||||
|
plugin_attrib = 'info'
|
||||||
|
plugin_multi_attrib = 'items'
|
||||||
|
interfaces = set(['bytes', 'height', 'id', 'type', 'url', 'width'])
|
||||||
|
|
||||||
|
|
||||||
|
class Pointer(ElementBase):
|
||||||
|
name = 'pointer'
|
||||||
|
namespace = 'urn:xmpp:avatar:metadata'
|
||||||
|
plugin_attrib = 'pointer'
|
||||||
|
plugin_multi_attrib = 'pointers'
|
||||||
|
interfaces = set()
|
||||||
|
|
||||||
|
|
||||||
|
register_stanza_plugin(MetaData, Info, iterable=True)
|
||||||
|
register_stanza_plugin(MetaData, Pointer, iterable=True)
|
||||||
@@ -43,6 +43,10 @@ class XEP_0085(BasePlugin):
|
|||||||
register_stanza_plugin(Message, stanza.Inactive)
|
register_stanza_plugin(Message, stanza.Inactive)
|
||||||
register_stanza_plugin(Message, stanza.Paused)
|
register_stanza_plugin(Message, stanza.Paused)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp.remove_handler('Chat State')
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp.plugin['xep_0030'].add_feature(ChatState.namespace)
|
self.xmpp.plugin['xep_0030'].add_feature(ChatState.namespace)
|
||||||
|
|
||||||
def _handle_chat_state(self, msg):
|
def _handle_chat_state(self, msg):
|
||||||
|
|||||||
@@ -47,28 +47,28 @@ class LegacyError(ElementBase):
|
|||||||
interfaces = set(('condition',))
|
interfaces = set(('condition',))
|
||||||
overrides = ['set_condition']
|
overrides = ['set_condition']
|
||||||
|
|
||||||
error_map = {'bad-request': ('modify','400'),
|
error_map = {'bad-request': ('modify', '400'),
|
||||||
'conflict': ('cancel','409'),
|
'conflict': ('cancel', '409'),
|
||||||
'feature-not-implemented': ('cancel','501'),
|
'feature-not-implemented': ('cancel', '501'),
|
||||||
'forbidden': ('auth','403'),
|
'forbidden': ('auth', '403'),
|
||||||
'gone': ('modify','302'),
|
'gone': ('modify', '302'),
|
||||||
'internal-server-error': ('wait','500'),
|
'internal-server-error': ('wait', '500'),
|
||||||
'item-not-found': ('cancel','404'),
|
'item-not-found': ('cancel', '404'),
|
||||||
'jid-malformed': ('modify','400'),
|
'jid-malformed': ('modify', '400'),
|
||||||
'not-acceptable': ('modify','406'),
|
'not-acceptable': ('modify', '406'),
|
||||||
'not-allowed': ('cancel','405'),
|
'not-allowed': ('cancel', '405'),
|
||||||
'not-authorized': ('auth','401'),
|
'not-authorized': ('auth', '401'),
|
||||||
'payment-required': ('auth','402'),
|
'payment-required': ('auth', '402'),
|
||||||
'recipient-unavailable': ('wait','404'),
|
'recipient-unavailable': ('wait', '404'),
|
||||||
'redirect': ('modify','302'),
|
'redirect': ('modify', '302'),
|
||||||
'registration-required': ('auth','407'),
|
'registration-required': ('auth', '407'),
|
||||||
'remote-server-not-found': ('cancel','404'),
|
'remote-server-not-found': ('cancel', '404'),
|
||||||
'remote-server-timeout': ('wait','504'),
|
'remote-server-timeout': ('wait', '504'),
|
||||||
'resource-constraint': ('wait','500'),
|
'resource-constraint': ('wait', '500'),
|
||||||
'service-unavailable': ('cancel','503'),
|
'service-unavailable': ('cancel', '503'),
|
||||||
'subscription-required': ('auth','407'),
|
'subscription-required': ('auth', '407'),
|
||||||
'undefined-condition': (None,'500'),
|
'undefined-condition': (None, '500'),
|
||||||
'unexpected-request': ('wait','400')}
|
'unexpected-request': ('wait', '400')}
|
||||||
|
|
||||||
def setup(self, xml):
|
def setup(self, xml):
|
||||||
"""Don't create XML for the plugin."""
|
"""Don't create XML for the plugin."""
|
||||||
|
|||||||
@@ -48,6 +48,11 @@ class XEP_0092(BasePlugin):
|
|||||||
|
|
||||||
register_stanza_plugin(Iq, Version)
|
register_stanza_plugin(Iq, Version)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp.remove_handler('Software Version')
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature='jabber:iq:version')
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp.plugin['xep_0030'].add_feature('jabber:iq:version')
|
self.xmpp.plugin['xep_0030'].add_feature('jabber:iq:version')
|
||||||
|
|
||||||
def _handle_version(self, iq):
|
def _handle_version(self, iq):
|
||||||
@@ -79,5 +84,7 @@ class XEP_0092(BasePlugin):
|
|||||||
result = iq.send()
|
result = iq.send()
|
||||||
|
|
||||||
if result and result['type'] != 'error':
|
if result and result['type'] != 'error':
|
||||||
return result['software_version'].values
|
values = result['software_version'].values
|
||||||
|
del values['lang']
|
||||||
|
return values
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ class XEP_0107(BasePlugin):
|
|||||||
|
|
||||||
def plugin_init(self):
|
def plugin_init(self):
|
||||||
register_stanza_plugin(Message, UserMood)
|
register_stanza_plugin(Message, UserMood)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=UserMood.namespace)
|
||||||
|
self.xmpp['xep_0163'].remove_interest(UserMood.namespace)
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp['xep_0163'].register_pep('user_mood', UserMood)
|
self.xmpp['xep_0163'].register_pep('user_mood', UserMood)
|
||||||
|
|
||||||
def publish_mood(self, value=None, text=None, options=None,
|
def publish_mood(self, value=None, text=None, options=None,
|
||||||
|
|||||||
@@ -26,7 +26,11 @@ class XEP_0108(BasePlugin):
|
|||||||
dependencies = set(['xep_0163'])
|
dependencies = set(['xep_0163'])
|
||||||
stanza = stanza
|
stanza = stanza
|
||||||
|
|
||||||
def plugin_init(self):
|
def plugin_end(self):
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=UserActivity.namespace)
|
||||||
|
self.xmpp['xep_0163'].remove_interest(UserActivity.namespace)
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp['xep_0163'].register_pep('user_activity', UserActivity)
|
self.xmpp['xep_0163'].register_pep('user_activity', UserActivity)
|
||||||
|
|
||||||
def publish_activity(self, general, specific=None, text=None, options=None,
|
def publish_activity(self, general, specific=None, text=None, options=None,
|
||||||
|
|||||||
@@ -73,16 +73,15 @@ class XEP_0115(BasePlugin):
|
|||||||
restart=False,
|
restart=False,
|
||||||
order=10010)
|
order=10010)
|
||||||
|
|
||||||
self.xmpp['xep_0030'].add_feature(stanza.Capabilities.namespace)
|
|
||||||
|
|
||||||
disco = self.xmpp['xep_0030']
|
disco = self.xmpp['xep_0030']
|
||||||
self.static = StaticCaps(self.xmpp, disco.static)
|
self.static = StaticCaps(self.xmpp, disco.static)
|
||||||
|
|
||||||
self.api.settings['client_bare'] = False
|
|
||||||
self.api.settings['component_bare'] = False
|
|
||||||
for op in self._disco_ops:
|
for op in self._disco_ops:
|
||||||
self.api.register(getattr(self.static, op), op, default=True)
|
self.api.register(getattr(self.static, op), op, default=True)
|
||||||
|
|
||||||
|
for op in ('supports', 'has_identity'):
|
||||||
|
self.xmpp['xep_0030'].api.register(getattr(self.static, op), op)
|
||||||
|
|
||||||
self._run_node_handler = disco._run_node_handler
|
self._run_node_handler = disco._run_node_handler
|
||||||
|
|
||||||
disco.cache_caps = self.cache_caps
|
disco.cache_caps = self.cache_caps
|
||||||
@@ -90,6 +89,19 @@ class XEP_0115(BasePlugin):
|
|||||||
disco.assign_verstring = self.assign_verstring
|
disco.assign_verstring = self.assign_verstring
|
||||||
disco.get_verstring = self.get_verstring
|
disco.get_verstring = self.get_verstring
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=stanza.Capabilities.namespace)
|
||||||
|
self.xmpp.del_filter('out', self._filter_add_caps)
|
||||||
|
self.xmpp.del_event_handler('entity_caps', self._process_caps)
|
||||||
|
self.xmpp.remove_handler('Entity Capabilities')
|
||||||
|
if not self.xmpp.is_component:
|
||||||
|
self.xmpp.unregister_feature('caps', 10010)
|
||||||
|
for op in ('supports', 'has_identity'):
|
||||||
|
self.xmpp['xep_0030'].restore_defaults(op)
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
|
self.xmpp['xep_0030'].add_feature(stanza.Capabilities.namespace)
|
||||||
|
|
||||||
def _filter_add_caps(self, stanza):
|
def _filter_add_caps(self, stanza):
|
||||||
if isinstance(stanza, Presence) and self.broadcast:
|
if isinstance(stanza, Presence) and self.broadcast:
|
||||||
ver = self.get_verstring(stanza['from'])
|
ver = self.get_verstring(stanza['from'])
|
||||||
@@ -173,7 +185,8 @@ class XEP_0115(BasePlugin):
|
|||||||
form_types.append(f_type)
|
form_types.append(f_type)
|
||||||
deduped_form_types.add(f_type)
|
deduped_form_types.add(f_type)
|
||||||
if len(form_types) != len(deduped_form_types):
|
if len(form_types) != len(deduped_form_types):
|
||||||
log.debug("Duplicated FORM_TYPE values, invalid for caps")
|
log.debug("Duplicated FORM_TYPE values, " + \
|
||||||
|
"invalid for caps")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if len(f_type) > 1:
|
if len(f_type) > 1:
|
||||||
@@ -183,7 +196,8 @@ class XEP_0115(BasePlugin):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
if stanza['fields']['FORM_TYPE']['type'] != 'hidden':
|
if stanza['fields']['FORM_TYPE']['type'] != 'hidden':
|
||||||
log.debug("Field FORM_TYPE type not 'hidden', ignoring form for caps")
|
log.debug("Field FORM_TYPE type not 'hidden', " + \
|
||||||
|
"ignoring form for caps")
|
||||||
caps.xml.remove(stanza.xml)
|
caps.xml.remove(stanza.xml)
|
||||||
else:
|
else:
|
||||||
log.debug("No FORM_TYPE found, ignoring form for caps")
|
log.debug("No FORM_TYPE found, ignoring form for caps")
|
||||||
|
|||||||
@@ -26,7 +26,11 @@ class XEP_0118(BasePlugin):
|
|||||||
dependencies = set(['xep_0163'])
|
dependencies = set(['xep_0163'])
|
||||||
stanza = stanza
|
stanza = stanza
|
||||||
|
|
||||||
def plugin_init(self):
|
def plugin_end(self):
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=UserTune.namespace)
|
||||||
|
self.xmpp['xep_0163'].remove_interest(UserTune.namespace)
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp['xep_0163'].register_pep('user_tune', UserTune)
|
self.xmpp['xep_0163'].register_pep('user_tune', UserTune)
|
||||||
|
|
||||||
def publish_tune(self, artist=None, length=None, rating=None, source=None,
|
def publish_tune(self, artist=None, length=None, rating=None, source=None,
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ class XEP_0128(BasePlugin):
|
|||||||
|
|
||||||
register_stanza_plugin(DiscoInfo, Form, iterable=True)
|
register_stanza_plugin(DiscoInfo, Form, iterable=True)
|
||||||
|
|
||||||
def post_init(self):
|
|
||||||
"""Handle cross-plugin dependencies."""
|
|
||||||
self.disco = self.xmpp['xep_0030']
|
self.disco = self.xmpp['xep_0030']
|
||||||
self.static = StaticExtendedDisco(self.disco.static)
|
self.static = StaticExtendedDisco(self.disco.static)
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,15 @@ class XEP_0153(BasePlugin):
|
|||||||
self.api.register(self._set_hash, 'set_hash', default=True)
|
self.api.register(self._set_hash, 'set_hash', default=True)
|
||||||
self.api.register(self._get_hash, 'get_hash', default=True)
|
self.api.register(self._get_hash, 'get_hash', default=True)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp.del_filter('out', self._update_presence)
|
||||||
|
self.xmpp.del_event_handler('session_start', self._start)
|
||||||
|
self.xmpp.del_event_handler('presence_available', self._recv_presence)
|
||||||
|
self.xmpp.del_event_handler('presence_dnd', self._recv_presence)
|
||||||
|
self.xmpp.del_event_handler('presence_xa', self._recv_presence)
|
||||||
|
self.xmpp.del_event_handler('presence_chat', self._recv_presence)
|
||||||
|
self.xmpp.del_event_handler('presence_away', self._recv_presence)
|
||||||
|
|
||||||
def set_avatar(self, jid=None, avatar=None, mtype=None, block=True,
|
def set_avatar(self, jid=None, avatar=None, mtype=None, block=True,
|
||||||
timeout=None, callback=None):
|
timeout=None, callback=None):
|
||||||
vcard = self.xmpp['xep_0054'].get_vcard(jid, cached=True)
|
vcard = self.xmpp['xep_0054'].get_vcard(jid, cached=True)
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class XEP_0163(BasePlugin):
|
|||||||
jid -- Optionally specify the JID.
|
jid -- Optionally specify the JID.
|
||||||
"""
|
"""
|
||||||
if not isinstance(namespace, set) and not isinstance(namespace, list):
|
if not isinstance(namespace, set) and not isinstance(namespace, list):
|
||||||
namespace = [namespace]
|
namespace = [namespace]
|
||||||
|
|
||||||
for ns in namespace:
|
for ns in namespace:
|
||||||
self.xmpp['xep_0030'].add_feature('%s+notify' % ns,
|
self.xmpp['xep_0030'].add_feature('%s+notify' % ns,
|
||||||
@@ -74,8 +74,8 @@ class XEP_0163(BasePlugin):
|
|||||||
be a list of such namespaces.
|
be a list of such namespaces.
|
||||||
jid -- Optionally specify the JID.
|
jid -- Optionally specify the JID.
|
||||||
"""
|
"""
|
||||||
if not isinstance(namespace, set) and not isinstance(namespace, list):
|
if not isinstance(namespace, (set, list)):
|
||||||
namespace = [namespace]
|
namespace = [namespace]
|
||||||
|
|
||||||
for ns in namespace:
|
for ns in namespace:
|
||||||
self.xmpp['xep_0030'].del_feature(jid=jid,
|
self.xmpp['xep_0030'].del_feature(jid=jid,
|
||||||
@@ -109,6 +109,7 @@ class XEP_0163(BasePlugin):
|
|||||||
node = stanza.namespace
|
node = stanza.namespace
|
||||||
|
|
||||||
return self.xmpp['xep_0060'].publish(ifrom, node,
|
return self.xmpp['xep_0060'].publish(ifrom, node,
|
||||||
|
id=id,
|
||||||
payload=stanza.xml,
|
payload=stanza.xml,
|
||||||
options=options,
|
options=options,
|
||||||
ifrom=ifrom,
|
ifrom=ifrom,
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ class XEP_0172(BasePlugin):
|
|||||||
def plugin_init(self):
|
def plugin_init(self):
|
||||||
register_stanza_plugin(Message, UserNick)
|
register_stanza_plugin(Message, UserNick)
|
||||||
register_stanza_plugin(Presence, UserNick)
|
register_stanza_plugin(Presence, UserNick)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=UserNick.namespace)
|
||||||
|
self.xmpp['xep_0163'].remove_interest(UserNick.namespace)
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp['xep_0163'].register_pep('user_nick', UserNick)
|
self.xmpp['xep_0163'].register_pep('user_nick', UserNick)
|
||||||
|
|
||||||
def publish_nick(self, nick=None, options=None, ifrom=None, block=True,
|
def publish_nick(self, nick=None, options=None, ifrom=None, block=True,
|
||||||
|
|||||||
@@ -48,6 +48,13 @@ class XEP_0184(BasePlugin):
|
|||||||
StanzaPath('message/request_receipt'),
|
StanzaPath('message/request_receipt'),
|
||||||
self._handle_receipt_request))
|
self._handle_receipt_request))
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp['xep_0030'].del_feature('urn:xmpp:receipts')
|
||||||
|
self.xmpp.del_filter('out', self._filter_add_receipt_request)
|
||||||
|
self.xmpp.remove_handler('Message Receipt')
|
||||||
|
self.xmpp.remove_handler('Message Receipt Request')
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp['xep_0030'].add_feature('urn:xmpp:receipts')
|
self.xmpp['xep_0030'].add_feature('urn:xmpp:receipts')
|
||||||
|
|
||||||
def ack(self, msg):
|
def ack(self, msg):
|
||||||
|
|||||||
16
sleekxmpp/plugins/xep_0186/__init__.py
Normal file
16
sleekxmpp/plugins/xep_0186/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.base import register_plugin
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.xep_0186 import stanza
|
||||||
|
from sleekxmpp.plugins.xep_0186.stanza import Invisible, Visible
|
||||||
|
from sleekxmpp.plugins.xep_0186.invisible_command import XEP_0186
|
||||||
|
|
||||||
|
|
||||||
|
register_plugin(XEP_0186)
|
||||||
44
sleekxmpp/plugins/xep_0186/invisible_command.py
Normal file
44
sleekxmpp/plugins/xep_0186/invisible_command.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from sleekxmpp import Iq
|
||||||
|
from sleekxmpp.plugins import BasePlugin
|
||||||
|
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||||
|
from sleekxmpp.plugins.xep_0186 import stanza, Visible, Invisible
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0186(BasePlugin):
|
||||||
|
|
||||||
|
name = 'xep_0186'
|
||||||
|
description = 'XEP-0186: Invisible Command'
|
||||||
|
dependencies = set(['xep_0030'])
|
||||||
|
|
||||||
|
def plugin_init(self):
|
||||||
|
register_stanza_plugin(Iq, Visible)
|
||||||
|
register_stanza_plugin(Iq, Invisible)
|
||||||
|
|
||||||
|
def set_invisible(self, ifrom=None, block=True, callback=None,
|
||||||
|
timeout=None):
|
||||||
|
iq = self.xmpp.Iq()
|
||||||
|
iq['type'] = 'set'
|
||||||
|
iq['from'] = ifrom
|
||||||
|
iq.enable('invisible')
|
||||||
|
iq.send(block=block, callback=callback, timeout=timeout)
|
||||||
|
|
||||||
|
def set_visible(self, ifrom=None, block=True, callback=None,
|
||||||
|
timeout=None):
|
||||||
|
iq = self.xmpp.Iq()
|
||||||
|
iq['type'] = 'set'
|
||||||
|
iq['from'] = ifrom
|
||||||
|
iq.enable('visible')
|
||||||
|
iq.send(block=block, callback=callback, timeout=timeout)
|
||||||
23
sleekxmpp/plugins/xep_0186/stanza.py
Normal file
23
sleekxmpp/plugins/xep_0186/stanza.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.xmlstream import ElementBase, ET
|
||||||
|
|
||||||
|
|
||||||
|
class Invisible(ElementBase):
|
||||||
|
name = 'invisible'
|
||||||
|
namespace = 'urn:xmpp:invisible:0'
|
||||||
|
plugin_attrib = 'invisible'
|
||||||
|
interfaces = set()
|
||||||
|
|
||||||
|
|
||||||
|
class Visible(ElementBase):
|
||||||
|
name = 'visible'
|
||||||
|
namespace = 'urn:xmpp:visible:0'
|
||||||
|
plugin_attrib = 'visible'
|
||||||
|
interfaces = set()
|
||||||
15
sleekxmpp/plugins/xep_0191/__init__.py
Normal file
15
sleekxmpp/plugins/xep_0191/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.base import register_plugin
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.xep_0191.stanza import Block, Unblock, BlockList
|
||||||
|
from sleekxmpp.plugins.xep_0191.blocking import XEP_0191
|
||||||
|
|
||||||
|
|
||||||
|
register_plugin(XEP_0191)
|
||||||
83
sleekxmpp/plugins/xep_0191/blocking.py
Normal file
83
sleekxmpp/plugins/xep_0191/blocking.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from sleekxmpp import Iq
|
||||||
|
from sleekxmpp.plugins import BasePlugin
|
||||||
|
from sleekxmpp.xmlstream.handler import Callback
|
||||||
|
from sleekxmpp.xmlstream.matcher import StanzaPath
|
||||||
|
from sleekxmpp.xmlstream import register_stanza_plugin, JID
|
||||||
|
from sleekxmpp.plugins.xep_0191 import stanza, Block, Unblock, BlockList
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0191(BasePlugin):
|
||||||
|
|
||||||
|
name = 'xep_0191'
|
||||||
|
description = 'XEP-0191: Blocking Command'
|
||||||
|
dependencies = set(['xep_0030'])
|
||||||
|
stanza = stanza
|
||||||
|
|
||||||
|
def plugin_init(self):
|
||||||
|
register_stanza_plugin(Iq, BlockList)
|
||||||
|
register_stanza_plugin(Iq, Block)
|
||||||
|
register_stanza_plugin(Iq, Unblock)
|
||||||
|
|
||||||
|
self.xmpp.register_handler(
|
||||||
|
Callback('Blocked Contact',
|
||||||
|
StanzaPath('iq@type=set/block'),
|
||||||
|
self._handle_blocked))
|
||||||
|
|
||||||
|
self.xmpp.register_handler(
|
||||||
|
Callback('Unblocked Contact',
|
||||||
|
StanzaPath('iq@type=set/unblock'),
|
||||||
|
self._handle_unblocked))
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp.remove_handler('Blocked Contact')
|
||||||
|
self.xmpp.remove_handler('Unblocked Contact')
|
||||||
|
|
||||||
|
def get_blocked(self, ifrom=None, block=True, timeout=None, callback=None):
|
||||||
|
iq = self.xmpp.Iq()
|
||||||
|
iq['type'] = 'get'
|
||||||
|
iq['from'] = 'ifrom'
|
||||||
|
iq.enable('blocklist')
|
||||||
|
return iq.send(block=block, timeout=timeout, callback=callback)
|
||||||
|
|
||||||
|
def block(self, jids, ifrom=None, block=True, timeout=None, callback=None):
|
||||||
|
iq = self.xmpp.Iq()
|
||||||
|
iq['type'] = 'set'
|
||||||
|
iq['from'] = ifrom
|
||||||
|
|
||||||
|
if not isinstance(jids, (set, list)):
|
||||||
|
jids = [jids]
|
||||||
|
|
||||||
|
iq['block']['items'] = jids
|
||||||
|
return iq.send(block=block, timeout=timeout, callback=callback)
|
||||||
|
|
||||||
|
def unblock(self, jids=None, ifrom=None, block=True, timeout=None, callback=None):
|
||||||
|
iq = self.xmpp.Iq()
|
||||||
|
iq['type'] = 'set'
|
||||||
|
iq['from'] = ifrom
|
||||||
|
|
||||||
|
if jids is None:
|
||||||
|
jids = []
|
||||||
|
if not isinstance(jids, (set, list)):
|
||||||
|
jids = [jids]
|
||||||
|
|
||||||
|
iq['unblock']['items'] = jids
|
||||||
|
return iq.send(block=block, timeout=timeout, callback=callback)
|
||||||
|
|
||||||
|
def _handle_blocked(self, iq):
|
||||||
|
self.xmpp.event('blocked', iq)
|
||||||
|
|
||||||
|
def _handle_unblocked(self, iq):
|
||||||
|
self.xmpp.event('unblocked', iq)
|
||||||
50
sleekxmpp/plugins/xep_0191/stanza.py
Normal file
50
sleekxmpp/plugins/xep_0191/stanza.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.xmlstream import ET, ElementBase, JID
|
||||||
|
|
||||||
|
|
||||||
|
class BlockList(ElementBase):
|
||||||
|
name = 'blocklist'
|
||||||
|
namespace = 'urn:xmpp:blocking'
|
||||||
|
plugin_attrib = 'blocklist'
|
||||||
|
interfaces = set(['items'])
|
||||||
|
|
||||||
|
def get_items(self):
|
||||||
|
result = set()
|
||||||
|
items = self.xml.findall('{%s}item' % self.namespace)
|
||||||
|
if items is not None:
|
||||||
|
for item in items:
|
||||||
|
jid = JID(item.attrib.get('jid', ''))
|
||||||
|
if jid:
|
||||||
|
result.add(jid)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def set_items(self, values):
|
||||||
|
self.del_items()
|
||||||
|
for jid in values:
|
||||||
|
if jid:
|
||||||
|
item = ET.Element('{%s}item' % self.namespace)
|
||||||
|
item.attrib['jid'] = JID(jid).full
|
||||||
|
self.xml.append(item)
|
||||||
|
|
||||||
|
def del_items(self):
|
||||||
|
items = self.xml.findall('{%s}item' % self.namespace)
|
||||||
|
if items is not None:
|
||||||
|
for item in items:
|
||||||
|
self.xml.remove(item)
|
||||||
|
|
||||||
|
|
||||||
|
class Block(BlockList):
|
||||||
|
name = 'block'
|
||||||
|
plugin_attrib = 'block'
|
||||||
|
|
||||||
|
|
||||||
|
class Unblock(BlockList):
|
||||||
|
name = 'unblock'
|
||||||
|
plugin_attrib = 'unblock'
|
||||||
@@ -82,7 +82,6 @@ class Resumed(StanzaBase):
|
|||||||
self._set_attr('h', str(val))
|
self._set_attr('h', str(val))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Failed(StanzaBase, Error):
|
class Failed(StanzaBase, Error):
|
||||||
name = 'failed'
|
name = 'failed'
|
||||||
namespace = 'urn:xmpp:sm:3'
|
namespace = 'urn:xmpp:sm:3'
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from sleekxmpp.plugins.xep_0198 import stanza
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
MAX_SEQ = 2**32
|
MAX_SEQ = 2 ** 32
|
||||||
|
|
||||||
|
|
||||||
class XEP_0198(BasePlugin):
|
class XEP_0198(BasePlugin):
|
||||||
@@ -133,6 +133,27 @@ class XEP_0198(BasePlugin):
|
|||||||
|
|
||||||
self.xmpp.add_event_handler('session_end', self.session_end)
|
self.xmpp.add_event_handler('session_end', self.session_end)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
if self.xmpp.is_component:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.xmpp.unregister_feature('sm', self.config.get('order', 10100))
|
||||||
|
self.xmpp.unregister_feature('sm', self.config.get('resume_order', 9000))
|
||||||
|
self.xmpp.del_event_handler('session_end', self.session_end)
|
||||||
|
self.xmpp.del_filter('in', self._handle_incoming)
|
||||||
|
self.xmpp.del_filter('out_sync', self._handle_outgoing)
|
||||||
|
self.xmpp.remove_handler('Stream Management Enabled')
|
||||||
|
self.xmpp.remove_handler('Stream Management Resumed')
|
||||||
|
self.xmpp.remove_handler('Stream Management Failed')
|
||||||
|
self.xmpp.remove_handler('Stream Management Ack')
|
||||||
|
self.xmpp.remove_handler('Stream Management Request Ack')
|
||||||
|
self.xmpp.remove_stanza(stanza.Enable)
|
||||||
|
self.xmpp.remove_stanza(stanza.Enabled)
|
||||||
|
self.xmpp.remove_stanza(stanza.Resume)
|
||||||
|
self.xmpp.remove_stanza(stanza.Resumed)
|
||||||
|
self.xmpp.remove_stanza(stanza.Ack)
|
||||||
|
self.xmpp.remove_stanza(stanza.RequestAck)
|
||||||
|
|
||||||
def session_end(self, event):
|
def session_end(self, event):
|
||||||
"""Reset stream management state."""
|
"""Reset stream management state."""
|
||||||
self.enabled.clear()
|
self.enabled.clear()
|
||||||
|
|||||||
@@ -74,6 +74,16 @@ class XEP_0199(BasePlugin):
|
|||||||
self.xmpp.add_event_handler('session_end',
|
self.xmpp.add_event_handler('session_end',
|
||||||
self._handle_session_end)
|
self._handle_session_end)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=Ping.namespace)
|
||||||
|
self.xmpp.remove_handler('Ping')
|
||||||
|
if self.keepalive:
|
||||||
|
self.xmpp.del_event_handler('session_start',
|
||||||
|
self._handle_keepalive)
|
||||||
|
self.xmpp.del_event_handler('session_end',
|
||||||
|
self._handle_session_end)
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp['xep_0030'].add_feature(Ping.namespace)
|
self.xmpp['xep_0030'].add_feature(Ping.namespace)
|
||||||
|
|
||||||
def _handle_keepalive(self, event):
|
def _handle_keepalive(self, event):
|
||||||
|
|||||||
@@ -40,8 +40,12 @@ class XEP_0202(BasePlugin):
|
|||||||
# custom function can be supplied which accepts
|
# custom function can be supplied which accepts
|
||||||
# the JID of the entity to query for the time.
|
# the JID of the entity to query for the time.
|
||||||
self.local_time = self.config.get('local_time', None)
|
self.local_time = self.config.get('local_time', None)
|
||||||
|
|
||||||
|
def default_local_time(jid):
|
||||||
|
return xep_0082.datetime(offset=self.tz_offset)
|
||||||
|
|
||||||
if not self.local_time:
|
if not self.local_time:
|
||||||
self.local_time = lambda x: xep_0082.datetime(offset=self.tz_offset)
|
self.local_time = default_local_time
|
||||||
|
|
||||||
self.xmpp.registerHandler(
|
self.xmpp.registerHandler(
|
||||||
Callback('Entity Time',
|
Callback('Entity Time',
|
||||||
@@ -49,6 +53,11 @@ class XEP_0202(BasePlugin):
|
|||||||
self._handle_time_request))
|
self._handle_time_request))
|
||||||
register_stanza_plugin(Iq, stanza.EntityTime)
|
register_stanza_plugin(Iq, stanza.EntityTime)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature='urn:xmpp:time')
|
||||||
|
self.xmpp.remove_handler('Entity Time')
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp['xep_0030'].add_feature('urn:xmpp:time')
|
self.xmpp['xep_0030'].add_feature('urn:xmpp:time')
|
||||||
|
|
||||||
def _handle_time_request(self, iq):
|
def _handle_time_request(self, iq):
|
||||||
|
|||||||
@@ -13,9 +13,7 @@ from sleekxmpp.plugins.xep_0203.stanza import Delay
|
|||||||
from sleekxmpp.plugins.xep_0203.delay import XEP_0203
|
from sleekxmpp.plugins.xep_0203.delay import XEP_0203
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
register_plugin(XEP_0203)
|
register_plugin(XEP_0203)
|
||||||
|
|
||||||
|
|
||||||
# Retain some backwards compatibility
|
# Retain some backwards compatibility
|
||||||
xep_0203 = XEP_0203
|
xep_0203 = XEP_0203
|
||||||
|
|||||||
16
sleekxmpp/plugins/xep_0221/__init__.py
Normal file
16
sleekxmpp/plugins/xep_0221/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.base import register_plugin
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.xep_0221 import stanza
|
||||||
|
from sleekxmpp.plugins.xep_0221.stanza import Media, URI
|
||||||
|
from sleekxmpp.plugins.xep_0221.media import XEP_0221
|
||||||
|
|
||||||
|
|
||||||
|
register_plugin(XEP_0221)
|
||||||
27
sleekxmpp/plugins/xep_0221/media.py
Normal file
27
sleekxmpp/plugins/xep_0221/media.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from sleekxmpp.plugins import BasePlugin
|
||||||
|
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||||
|
from sleekxmpp.plugins.xep_0221 import stanza, Media, URI
|
||||||
|
from sleekxmpp.plugins.xep_0004 import FormField
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0221(BasePlugin):
|
||||||
|
|
||||||
|
name = 'xep_0221'
|
||||||
|
description = 'XEP-0221: Data Forms Media Element'
|
||||||
|
dependencies = set(['xep_0004'])
|
||||||
|
|
||||||
|
def plugin_init(self):
|
||||||
|
register_stanza_plugin(FormField, Media)
|
||||||
42
sleekxmpp/plugins/xep_0221/stanza.py
Normal file
42
sleekxmpp/plugins/xep_0221/stanza.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.xmlstream import ElementBase, register_stanza_plugin
|
||||||
|
|
||||||
|
|
||||||
|
class Media(ElementBase):
|
||||||
|
name = 'media'
|
||||||
|
namespace = 'urn:xmpp:media-element'
|
||||||
|
plugin_attrib = 'media'
|
||||||
|
interfaces = set(['height', 'width', 'alt'])
|
||||||
|
|
||||||
|
def add_uri(self, value, itype):
|
||||||
|
uri = URI()
|
||||||
|
uri['value'] = value
|
||||||
|
uri['type'] = itype
|
||||||
|
self.append(uri)
|
||||||
|
|
||||||
|
|
||||||
|
class URI(ElementBase):
|
||||||
|
name = 'uri'
|
||||||
|
namespace = 'urn:xmpp:media-element'
|
||||||
|
plugin_attrib = 'uri'
|
||||||
|
plugin_multi_attrib = 'uris'
|
||||||
|
interfaces = set(['type', 'value'])
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
return self.xml.text
|
||||||
|
|
||||||
|
def set_value(self, value):
|
||||||
|
self.xml.text = value
|
||||||
|
|
||||||
|
def del_value(self):
|
||||||
|
sel.xml.text = ''
|
||||||
|
|
||||||
|
|
||||||
|
register_stanza_plugin(Media, URI, iterable=True)
|
||||||
126
sleekxmpp/plugins/xep_0222.py
Normal file
126
sleekxmpp/plugins/xep_0222.py
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||||
|
from sleekxmpp.plugins.base import BasePlugin, register_plugin
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0222(BasePlugin):
|
||||||
|
|
||||||
|
"""
|
||||||
|
XEP-0222: Persistent Storage of Public Data via PubSub
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = 'xep_0222'
|
||||||
|
description = 'XEP-0222: Persistent Storage of Private Data via PubSub'
|
||||||
|
dependencies = set(['xep_0163', 'xep_0060', 'xep_0004'])
|
||||||
|
|
||||||
|
profile = {'pubsub#persist_items': True,
|
||||||
|
'pubsub#send_last_published_item': 'never'}
|
||||||
|
|
||||||
|
def configure(self, node):
|
||||||
|
"""
|
||||||
|
Update a node's configuration to match the public storage profile.
|
||||||
|
"""
|
||||||
|
config = self.xmpp['xep_0004'].Form()
|
||||||
|
config['type'] = 'submit'
|
||||||
|
|
||||||
|
for field, value in self.profile.items():
|
||||||
|
config.add_field(var=field, value=value)
|
||||||
|
|
||||||
|
return self.xmpp['xep_0060'].set_node_config(None, node, config,
|
||||||
|
ifrom=ifrom,
|
||||||
|
block=block,
|
||||||
|
callback=callback,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
def store(self, stanza, node=None, id=None, ifrom=None, options=None,
|
||||||
|
block=True, callback=None, timeout=None):
|
||||||
|
"""
|
||||||
|
Store public data via PEP.
|
||||||
|
|
||||||
|
This is just a (very) thin wrapper around the XEP-0060 publish()
|
||||||
|
method to set the defaults expected by PEP.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
stanza -- The private content to store.
|
||||||
|
node -- The node to publish the content to. If not specified,
|
||||||
|
the stanza's namespace will be used.
|
||||||
|
id -- Optionally specify the ID of the item.
|
||||||
|
options -- Publish options to use, which will be modified to
|
||||||
|
fit the persistent storage option profile.
|
||||||
|
ifrom -- Specify the sender's JID.
|
||||||
|
block -- Specify if the send call will block until a response
|
||||||
|
is received, or a timeout occurs. Defaults to True.
|
||||||
|
timeout -- The length of time (in seconds) to wait for a response
|
||||||
|
before exiting the send call if blocking is used.
|
||||||
|
Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
|
||||||
|
callback -- Optional reference to a stream handler function. Will
|
||||||
|
be executed when a reply stanza is received.
|
||||||
|
"""
|
||||||
|
if not options:
|
||||||
|
options = self.xmpp['xep_0004'].stanza.Form()
|
||||||
|
options['type'] = 'submit'
|
||||||
|
options.add_field(
|
||||||
|
var='FORM_TYPE',
|
||||||
|
ftype='hidden',
|
||||||
|
value='http://jabber.org/protocol/pubsub#publish-options')
|
||||||
|
|
||||||
|
for field, value in self.profile.items():
|
||||||
|
if field not in options.fields:
|
||||||
|
options.add_field(var=field)
|
||||||
|
options.fields[field]['value'] = value
|
||||||
|
|
||||||
|
return self.xmpp['xep_0163'].publish(stanza, node,
|
||||||
|
options=options,
|
||||||
|
ifrom=ifrom,
|
||||||
|
block=block,
|
||||||
|
callback=callback,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
def retrieve(self, node, id=None, item_ids=None, ifrom=None,
|
||||||
|
block=True, callback=None, timeout=None):
|
||||||
|
"""
|
||||||
|
Retrieve public data via PEP.
|
||||||
|
|
||||||
|
This is just a (very) thin wrapper around the XEP-0060 publish()
|
||||||
|
method to set the defaults expected by PEP.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
node -- The node to retrieve content from.
|
||||||
|
id -- Optionally specify the ID of the item.
|
||||||
|
item_ids -- Specify a group of IDs. If id is also specified, it
|
||||||
|
will be included in item_ids.
|
||||||
|
ifrom -- Specify the sender's JID.
|
||||||
|
block -- Specify if the send call will block until a response
|
||||||
|
is received, or a timeout occurs. Defaults to True.
|
||||||
|
timeout -- The length of time (in seconds) to wait for a response
|
||||||
|
before exiting the send call if blocking is used.
|
||||||
|
Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
|
||||||
|
callback -- Optional reference to a stream handler function. Will
|
||||||
|
be executed when a reply stanza is received.
|
||||||
|
"""
|
||||||
|
if item_ids is None:
|
||||||
|
item_ids = []
|
||||||
|
if id is not None:
|
||||||
|
item_ids.append(id)
|
||||||
|
|
||||||
|
return self.xmpp['xep_0060'].get_items(None, node,
|
||||||
|
item_ids=item_ids,
|
||||||
|
ifrom=ifrom,
|
||||||
|
block=block,
|
||||||
|
callback=callback,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
|
||||||
|
register_plugin(XEP_0222)
|
||||||
126
sleekxmpp/plugins/xep_0223.py
Normal file
126
sleekxmpp/plugins/xep_0223.py
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||||
|
from sleekxmpp.plugins.base import BasePlugin, register_plugin
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0223(BasePlugin):
|
||||||
|
|
||||||
|
"""
|
||||||
|
XEP-0223: Persistent Storage of Private Data via PubSub
|
||||||
|
"""
|
||||||
|
|
||||||
|
name = 'xep_0223'
|
||||||
|
description = 'XEP-0223: Persistent Storage of Private Data via PubSub'
|
||||||
|
dependencies = set(['xep_0163', 'xep_0060', 'xep_0004'])
|
||||||
|
|
||||||
|
profile = {'pubsub#persist_items': True,
|
||||||
|
'pubsub#send_last_published_item': 'never'}
|
||||||
|
|
||||||
|
def configure(self, node):
|
||||||
|
"""
|
||||||
|
Update a node's configuration to match the public storage profile.
|
||||||
|
"""
|
||||||
|
config = self.xmpp['xep_0004'].Form()
|
||||||
|
config['type'] = 'submit'
|
||||||
|
|
||||||
|
for field, value in self.profile.items():
|
||||||
|
config.add_field(var=field, value=value)
|
||||||
|
|
||||||
|
return self.xmpp['xep_0060'].set_node_config(None, node, config,
|
||||||
|
ifrom=ifrom,
|
||||||
|
block=block,
|
||||||
|
callback=callback,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
def store(self, stanza, node=None, id=None, ifrom=None, options=None,
|
||||||
|
block=True, callback=None, timeout=None):
|
||||||
|
"""
|
||||||
|
Store private data via PEP.
|
||||||
|
|
||||||
|
This is just a (very) thin wrapper around the XEP-0060 publish()
|
||||||
|
method to set the defaults expected by PEP.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
stanza -- The private content to store.
|
||||||
|
node -- The node to publish the content to. If not specified,
|
||||||
|
the stanza's namespace will be used.
|
||||||
|
id -- Optionally specify the ID of the item.
|
||||||
|
options -- Publish options to use, which will be modified to
|
||||||
|
fit the persistent storage option profile.
|
||||||
|
ifrom -- Specify the sender's JID.
|
||||||
|
block -- Specify if the send call will block until a response
|
||||||
|
is received, or a timeout occurs. Defaults to True.
|
||||||
|
timeout -- The length of time (in seconds) to wait for a response
|
||||||
|
before exiting the send call if blocking is used.
|
||||||
|
Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
|
||||||
|
callback -- Optional reference to a stream handler function. Will
|
||||||
|
be executed when a reply stanza is received.
|
||||||
|
"""
|
||||||
|
if not options:
|
||||||
|
options = self.xmpp['xep_0004'].stanza.Form()
|
||||||
|
options['type'] = 'submit'
|
||||||
|
options.add_field(
|
||||||
|
var='FORM_TYPE',
|
||||||
|
ftype='hidden',
|
||||||
|
value='http://jabber.org/protocol/pubsub#publish-options')
|
||||||
|
|
||||||
|
for field, value in self.profile.items():
|
||||||
|
if field not in options.fields:
|
||||||
|
options.add_field(var=field)
|
||||||
|
options.fields[field]['value'] = value
|
||||||
|
|
||||||
|
return self.xmpp['xep_0163'].publish(stanza, node,
|
||||||
|
options=options,
|
||||||
|
ifrom=ifrom,
|
||||||
|
block=block,
|
||||||
|
callback=callback,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
def retrieve(self, node, id=None, item_ids=None, ifrom=None,
|
||||||
|
block=True, callback=None, timeout=None):
|
||||||
|
"""
|
||||||
|
Retrieve private data via PEP.
|
||||||
|
|
||||||
|
This is just a (very) thin wrapper around the XEP-0060 publish()
|
||||||
|
method to set the defaults expected by PEP.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
node -- The node to retrieve content from.
|
||||||
|
id -- Optionally specify the ID of the item.
|
||||||
|
item_ids -- Specify a group of IDs. If id is also specified, it
|
||||||
|
will be included in item_ids.
|
||||||
|
ifrom -- Specify the sender's JID.
|
||||||
|
block -- Specify if the send call will block until a response
|
||||||
|
is received, or a timeout occurs. Defaults to True.
|
||||||
|
timeout -- The length of time (in seconds) to wait for a response
|
||||||
|
before exiting the send call if blocking is used.
|
||||||
|
Defaults to sleekxmpp.xmlstream.RESPONSE_TIMEOUT
|
||||||
|
callback -- Optional reference to a stream handler function. Will
|
||||||
|
be executed when a reply stanza is received.
|
||||||
|
"""
|
||||||
|
if item_ids is None:
|
||||||
|
item_ids = []
|
||||||
|
if id is not None:
|
||||||
|
item_ids.append(id)
|
||||||
|
|
||||||
|
return self.xmpp['xep_0060'].get_items(None, node,
|
||||||
|
item_ids=item_ids,
|
||||||
|
ifrom=ifrom,
|
||||||
|
block=block,
|
||||||
|
callback=callback,
|
||||||
|
timeout=timeout)
|
||||||
|
|
||||||
|
|
||||||
|
register_plugin(XEP_0223)
|
||||||
@@ -39,6 +39,11 @@ class XEP_0224(BasePlugin):
|
|||||||
StanzaPath('message/attention'),
|
StanzaPath('message/attention'),
|
||||||
self._handle_attention))
|
self._handle_attention))
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=stanza.Attention.namespace)
|
||||||
|
self.xmpp.remove_handler('Attention')
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp['xep_0030'].add_feature(stanza.Attention.namespace)
|
self.xmpp['xep_0030'].add_feature(stanza.Attention.namespace)
|
||||||
|
|
||||||
def request_attention(self, to, mfrom=None, mbody=''):
|
def request_attention(self, to, mfrom=None, mbody=''):
|
||||||
|
|||||||
@@ -35,8 +35,6 @@ class XEP_0231(BasePlugin):
|
|||||||
def plugin_init(self):
|
def plugin_init(self):
|
||||||
self._cids = {}
|
self._cids = {}
|
||||||
|
|
||||||
self.xmpp['xep_0030'].add_feature('urn:xmpp:bob')
|
|
||||||
|
|
||||||
register_stanza_plugin(Iq, BitsOfBinary)
|
register_stanza_plugin(Iq, BitsOfBinary)
|
||||||
|
|
||||||
self.xmpp.register_handler(
|
self.xmpp.register_handler(
|
||||||
@@ -58,6 +56,14 @@ class XEP_0231(BasePlugin):
|
|||||||
self.api.register(self._set_bob, 'set_bob', default=True)
|
self.api.register(self._set_bob, 'set_bob', default=True)
|
||||||
self.api.register(self._del_bob, 'del_bob', default=True)
|
self.api.register(self._del_bob, 'del_bob', default=True)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature='urn:xmpp:bob')
|
||||||
|
self.xmpp.remove_handler('Bits of Binary - Iq')
|
||||||
|
self.xmpp.remove_handler('Bits of Binary - Message')
|
||||||
|
self.xmpp.remove_handler('Bits of Binary - Presence')
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
|
self.xmpp['xep_0030'].add_feature('urn:xmpp:bob')
|
||||||
|
|
||||||
def set_bob(self, data, mtype, cid=None, max_age=None):
|
def set_bob(self, data, mtype, cid=None, max_age=None):
|
||||||
if cid is None:
|
if cid is None:
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ class XEP_0249(BasePlugin):
|
|||||||
|
|
||||||
register_stanza_plugin(Message, Invite)
|
register_stanza_plugin(Message, Invite)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=Invite.namespace)
|
||||||
|
self.xmpp.remove_handler('Direct MUC Invitations')
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
self.xmpp['xep_0030'].add_feature(Invite.namespace)
|
self.xmpp['xep_0030'].add_feature(Invite.namespace)
|
||||||
|
|
||||||
def _handle_invite(self, msg):
|
def _handle_invite(self, msg):
|
||||||
|
|||||||
72
sleekxmpp/plugins/xep_0256.py
Normal file
72
sleekxmpp/plugins/xep_0256.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from sleekxmpp import Presence
|
||||||
|
from sleekxmpp.exceptions import XMPPError
|
||||||
|
from sleekxmpp.plugins import BasePlugin, register_plugin
|
||||||
|
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.xep_0012 import stanza, LastActivity
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0256(BasePlugin):
|
||||||
|
|
||||||
|
name = 'xep_0256'
|
||||||
|
description = 'XEP-0256: Last Activity in Presence'
|
||||||
|
dependencies = set(['xep_0012'])
|
||||||
|
stanza = stanza
|
||||||
|
|
||||||
|
def plugin_init(self):
|
||||||
|
self.auto_last_activity = self.config.get('auto_last_activity', False)
|
||||||
|
|
||||||
|
register_stanza_plugin(Presence, LastActivity)
|
||||||
|
|
||||||
|
self.xmpp.add_filter('out', self._initial_presence_activity)
|
||||||
|
self.xmpp.add_event_handler('connected', self._reset_presence_activity)
|
||||||
|
|
||||||
|
self._initial_presence = set()
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp.del_filter('out', self._initial_presence_activity)
|
||||||
|
self.xmpp.del_event_handler('connected', self._reset_presence_activity)
|
||||||
|
|
||||||
|
def _reset_presence_activity(self, e):
|
||||||
|
self._initial_presence = set()
|
||||||
|
|
||||||
|
def _initial_presence_activity(self, stanza):
|
||||||
|
if isinstance(stanza, Presence):
|
||||||
|
use_last_activity = False
|
||||||
|
|
||||||
|
if self.auto_last_activity and stanza['show'] in ('xa', 'away'):
|
||||||
|
use_last_activity = True
|
||||||
|
|
||||||
|
if stanza['from'] not in self._initial_presence:
|
||||||
|
self._initial_presence.add(stanza['from'])
|
||||||
|
use_last_activity = True
|
||||||
|
|
||||||
|
if use_last_activity:
|
||||||
|
plugin = self.xmpp['xep_0012']
|
||||||
|
try:
|
||||||
|
result = plugin.api['get_last_activity'](stanza['from'],
|
||||||
|
None,
|
||||||
|
stanza['to'])
|
||||||
|
seconds = result['last_activity']['seconds']
|
||||||
|
except XMPPError:
|
||||||
|
seconds = None
|
||||||
|
|
||||||
|
if seconds is not None:
|
||||||
|
stanza['last_activity']['seconds'] = seconds
|
||||||
|
return stanza
|
||||||
|
|
||||||
|
|
||||||
|
register_plugin(XEP_0256)
|
||||||
18
sleekxmpp/plugins/xep_0258/__init__.py
Normal file
18
sleekxmpp/plugins/xep_0258/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.base import register_plugin
|
||||||
|
|
||||||
|
from sleekxmpp.plugins.xep_0258 import stanza
|
||||||
|
from sleekxmpp.plugins.xep_0258.stanza import SecurityLabel, Label
|
||||||
|
from sleekxmpp.plugins.xep_0258.stanza import DisplayMarking, EquivalentLabel
|
||||||
|
from sleekxmpp.plugins.xep_0258.stanza import ESSLabel, Catalog, CatalogItem
|
||||||
|
from sleekxmpp.plugins.xep_0258.security_labels import XEP_0258
|
||||||
|
|
||||||
|
|
||||||
|
register_plugin(XEP_0258)
|
||||||
44
sleekxmpp/plugins/xep_0258/security_labels.py
Normal file
44
sleekxmpp/plugins/xep_0258/security_labels.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from sleekxmpp import Iq, Message
|
||||||
|
from sleekxmpp.plugins import BasePlugin
|
||||||
|
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||||
|
from sleekxmpp.plugins.xep_0258 import stanza, SecurityLabel, Catalog
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0258(BasePlugin):
|
||||||
|
|
||||||
|
name = 'xep_0258'
|
||||||
|
description = 'XEP-0258: Security Labels in XMPP'
|
||||||
|
dependencies = set(['xep_0030'])
|
||||||
|
stanza = stanza
|
||||||
|
|
||||||
|
def plugin_init(self):
|
||||||
|
register_stanza_plugin(Message, SecurityLabel)
|
||||||
|
register_stanza_plugin(Iq, Catalog)
|
||||||
|
|
||||||
|
def plugin_end(self):
|
||||||
|
self.xmpp['xep_0030'].del_feature(feature=SecurityLabel.namespace)
|
||||||
|
|
||||||
|
def session_bind(self, jid):
|
||||||
|
self.xmpp['xep_0030'].add_feature(SecurityLabel.namespace)
|
||||||
|
|
||||||
|
def get_catalog(self, jid, ifrom=None, block=True,
|
||||||
|
callback=None, timeout=None):
|
||||||
|
iq = self.xmpp.Iq()
|
||||||
|
iq['to'] = jid
|
||||||
|
iq['from'] = ifrom
|
||||||
|
iq['type'] = 'get'
|
||||||
|
iq.enable('security_label_catalog')
|
||||||
|
return iq.send(block=block, callback=callback, timeout=timeout)
|
||||||
142
sleekxmpp/plugins/xep_0258/stanza.py
Normal file
142
sleekxmpp/plugins/xep_0258/stanza.py
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from base64 import b64encode, b64decode
|
||||||
|
|
||||||
|
from sleekxmpp.thirdparty.suelta.util import bytes
|
||||||
|
|
||||||
|
from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin
|
||||||
|
|
||||||
|
|
||||||
|
class SecurityLabel(ElementBase):
|
||||||
|
name = 'securitylabel'
|
||||||
|
namespace = 'urn:xmpp:sec-label:0'
|
||||||
|
plugin_attrib = 'security_label'
|
||||||
|
|
||||||
|
def add_equivalent(self, label):
|
||||||
|
equiv = EquivalentLabel(parent=self)
|
||||||
|
equiv.append(label)
|
||||||
|
return equiv
|
||||||
|
|
||||||
|
|
||||||
|
class Label(ElementBase):
|
||||||
|
name = 'label'
|
||||||
|
namespace = 'urn:xmpp:sec-label:0'
|
||||||
|
plugin_attrib = 'label'
|
||||||
|
|
||||||
|
|
||||||
|
class DisplayMarking(ElementBase):
|
||||||
|
name = 'displaymarking'
|
||||||
|
namespace = 'urn:xmpp:sec-label:0'
|
||||||
|
plugin_attrib = 'display_marking'
|
||||||
|
interfaces = set(['fgcolor', 'bgcolor', 'value'])
|
||||||
|
|
||||||
|
def get_fgcolor(self):
|
||||||
|
return self._get_attr('fgcolor', 'black')
|
||||||
|
|
||||||
|
def get_bgcolor(self):
|
||||||
|
return self._get_attr('fgcolor', 'white')
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
return self.xml.text
|
||||||
|
|
||||||
|
def set_value(self, value):
|
||||||
|
self.xml.text = value
|
||||||
|
|
||||||
|
def del_value(self):
|
||||||
|
self.xml.text = ''
|
||||||
|
|
||||||
|
|
||||||
|
class EquivalentLabel(ElementBase):
|
||||||
|
name = 'equivalentlabel'
|
||||||
|
namespace = 'urn:xmpp:sec-label:0'
|
||||||
|
plugin_attrib = 'equivalent_label'
|
||||||
|
plugin_multi_attrib = 'equivalent_labels'
|
||||||
|
|
||||||
|
|
||||||
|
class Catalog(ElementBase):
|
||||||
|
name = 'catalog'
|
||||||
|
namespace = 'urn:xmpp:sec-label:catalog:2'
|
||||||
|
plugin_attrib = 'security_label_catalog'
|
||||||
|
interfaces = set(['to', 'from', 'name', 'desc', 'id', 'size', 'restrict'])
|
||||||
|
|
||||||
|
def get_to(self):
|
||||||
|
return JID(self._get_attr('to'))
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_to(self, value):
|
||||||
|
return self._set_attr('to', str(value))
|
||||||
|
|
||||||
|
def get_from(self):
|
||||||
|
return JID(self._get_attr('from'))
|
||||||
|
|
||||||
|
def set_from(self, value):
|
||||||
|
return self._set_attr('from', str(value))
|
||||||
|
|
||||||
|
def get_restrict(self):
|
||||||
|
value = self._get_attr('restrict', '')
|
||||||
|
if value and value.lower() in ('true', '1'):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def set_restrict(self, value):
|
||||||
|
self._del_attr('restrict')
|
||||||
|
if value:
|
||||||
|
self._set_attr('restrict', 'true')
|
||||||
|
elif value is False:
|
||||||
|
self._set_attr('restrict', 'false')
|
||||||
|
|
||||||
|
|
||||||
|
class CatalogItem(ElementBase):
|
||||||
|
name = 'catalog'
|
||||||
|
namespace = 'urn:xmpp:sec-label:catalog:2'
|
||||||
|
plugin_attrib = 'item'
|
||||||
|
plugin_multi_attrib = 'items'
|
||||||
|
interfaces = set(['selector', 'default'])
|
||||||
|
|
||||||
|
def get_default(self):
|
||||||
|
value = self._get_attr('default', '')
|
||||||
|
if value.lower() in ('true', '1'):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def set_default(self, value):
|
||||||
|
self._del_attr('default')
|
||||||
|
if value:
|
||||||
|
self._set_attr('default', 'true')
|
||||||
|
elif value is False:
|
||||||
|
self._set_attr('default', 'false')
|
||||||
|
|
||||||
|
|
||||||
|
class ESSLabel(ElementBase):
|
||||||
|
name = 'esssecuritylabel'
|
||||||
|
namespace = 'urn:xmpp:sec-label:ess:0'
|
||||||
|
plugin_attrib = 'ess'
|
||||||
|
interfaces = set(['value'])
|
||||||
|
|
||||||
|
def get_value(self):
|
||||||
|
if self.xml.text:
|
||||||
|
return b64decode(bytes(self.xml.text))
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def set_value(self, value):
|
||||||
|
self.xml.text = ''
|
||||||
|
if value:
|
||||||
|
self.xml.text = b64encode(bytes(value))
|
||||||
|
|
||||||
|
def del_value(self):
|
||||||
|
self.xml.text = ''
|
||||||
|
|
||||||
|
|
||||||
|
register_stanza_plugin(Catalog, CatalogItem, iterable=True)
|
||||||
|
register_stanza_plugin(CatalogItem, SecurityLabel)
|
||||||
|
register_stanza_plugin(EquivalentLabel, ESSLabel)
|
||||||
|
register_stanza_plugin(Label, ESSLabel)
|
||||||
|
register_stanza_plugin(SecurityLabel, DisplayMarking)
|
||||||
|
register_stanza_plugin(SecurityLabel, EquivalentLabel, iterable=True)
|
||||||
|
register_stanza_plugin(SecurityLabel, Label)
|
||||||
20
sleekxmpp/plugins/xep_0270.py
Normal file
20
sleekxmpp/plugins/xep_0270.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.plugins import BasePlugin, register_plugin
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0270(BasePlugin):
|
||||||
|
|
||||||
|
name = 'xep_0270'
|
||||||
|
description = 'XEP-0270: XMPP Compliance Suites 2010'
|
||||||
|
dependencies = set(['xep_0030', 'xep_0115', 'xep_0054',
|
||||||
|
'xep_0163', 'xep_0045', 'xep_0085'])
|
||||||
|
|
||||||
|
|
||||||
|
register_plugin(XEP_0270)
|
||||||
21
sleekxmpp/plugins/xep_0302.py
Normal file
21
sleekxmpp/plugins/xep_0302.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
See the file LICENSE for copying permission.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.plugins import BasePlugin, register_plugin
|
||||||
|
|
||||||
|
|
||||||
|
class XEP_0302(BasePlugin):
|
||||||
|
|
||||||
|
name = 'xep_0302'
|
||||||
|
description = 'XEP-0302: XMPP Compliance Suites 2012'
|
||||||
|
dependencies = set(['xep_0030', 'xep_0115', 'xep_0054',
|
||||||
|
'xep_0163', 'xep_0045', 'xep_0085',
|
||||||
|
'xep_0184', 'xep_0198'])
|
||||||
|
|
||||||
|
|
||||||
|
register_plugin(XEP_0302)
|
||||||
@@ -307,34 +307,29 @@ class RosterItem(object):
|
|||||||
p['from'] = self.owner
|
p['from'] = self.owner
|
||||||
p.send()
|
p.send()
|
||||||
|
|
||||||
def send_presence(self, ptype=None, pshow=None, pstatus=None,
|
def send_presence(self, **kwargs):
|
||||||
ppriority=None, pnick=None):
|
|
||||||
"""
|
"""
|
||||||
Create, initialize, and send a Presence stanza.
|
Create, initialize, and send a Presence stanza.
|
||||||
|
|
||||||
|
If no recipient is specified, send the presence immediately.
|
||||||
|
Otherwise, forward the send request to the recipient's roster
|
||||||
|
entry for processing.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
pshow -- The presence's show value.
|
pshow -- The presence's show value.
|
||||||
pstatus -- The presence's status message.
|
pstatus -- The presence's status message.
|
||||||
ppriority -- This connections' priority.
|
ppriority -- This connections' priority.
|
||||||
|
pto -- The recipient of a directed presence.
|
||||||
|
pfrom -- The sender of a directed presence, which should
|
||||||
|
be the owner JID plus resource.
|
||||||
ptype -- The type of presence, such as 'subscribe'.
|
ptype -- The type of presence, such as 'subscribe'.
|
||||||
pnick -- Optional nickname of the presence's sender.
|
pnick -- Optional nickname of the presence's sender.
|
||||||
"""
|
"""
|
||||||
p = self.xmpp.make_presence(pshow=pshow,
|
if self.xmpp.is_component and not kwargs.get('pfrom', ''):
|
||||||
pstatus=pstatus,
|
kwargs['pfrom'] = self.owner
|
||||||
ppriority=ppriority,
|
if not kwargs.get('pto', ''):
|
||||||
ptype=ptype,
|
kwargs['pto'] = self.jid
|
||||||
pnick=pnick,
|
self.xmpp.send_presence(**kwargs)
|
||||||
pto=self.jid)
|
|
||||||
if self.xmpp.is_component:
|
|
||||||
p['from'] = self.owner
|
|
||||||
if p['type'] in p.showtypes or \
|
|
||||||
p['type'] in ['available', 'unavailable']:
|
|
||||||
self.last_status = p
|
|
||||||
p.send()
|
|
||||||
|
|
||||||
if not self.xmpp.sentpresence:
|
|
||||||
self.xmpp.event('sent_presence')
|
|
||||||
self.xmpp.sentpresence = True
|
|
||||||
|
|
||||||
def send_last_presence(self):
|
def send_last_presence(self):
|
||||||
if self.last_status is None:
|
if self.last_status is None:
|
||||||
|
|||||||
@@ -6,9 +6,11 @@
|
|||||||
See the file LICENSE for copying permission.
|
See the file LICENSE for copying permission.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.stanza import Presence
|
||||||
from sleekxmpp.xmlstream import JID
|
from sleekxmpp.xmlstream import JID
|
||||||
from sleekxmpp.roster import RosterNode
|
from sleekxmpp.roster import RosterNode
|
||||||
|
|
||||||
|
|
||||||
class Roster(object):
|
class Roster(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -55,6 +57,33 @@ class Roster(object):
|
|||||||
for node in self.db.entries(None, {}):
|
for node in self.db.entries(None, {}):
|
||||||
self.add(node)
|
self.add(node)
|
||||||
|
|
||||||
|
self.xmpp.add_filter('out', self._save_last_status)
|
||||||
|
|
||||||
|
def _save_last_status(self, stanza):
|
||||||
|
|
||||||
|
if isinstance(stanza, Presence):
|
||||||
|
sfrom = stanza['from'].full
|
||||||
|
sto = stanza['to'].full
|
||||||
|
|
||||||
|
if not sfrom:
|
||||||
|
sfrom = self.xmpp.boundjid
|
||||||
|
|
||||||
|
if stanza['type'] in stanza.showtypes or \
|
||||||
|
stanza['type'] in ('available', 'unavailable'):
|
||||||
|
if sto:
|
||||||
|
self[sfrom][sto].last_status = stanza
|
||||||
|
else:
|
||||||
|
self[sfrom].last_status = stanza
|
||||||
|
with self[sfrom]._last_status_lock:
|
||||||
|
for jid in self[sfrom]:
|
||||||
|
self[sfrom][jid].last_status = None
|
||||||
|
|
||||||
|
if not self.xmpp.sentpresence:
|
||||||
|
self.xmpp.event('sent_presence')
|
||||||
|
self.xmpp.sentpresence = True
|
||||||
|
|
||||||
|
return stanza
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
"""
|
"""
|
||||||
Return the roster node for a JID.
|
Return the roster node for a JID.
|
||||||
@@ -65,10 +94,12 @@ class Roster(object):
|
|||||||
Arguments:
|
Arguments:
|
||||||
key -- Return the roster for this JID.
|
key -- Return the roster for this JID.
|
||||||
"""
|
"""
|
||||||
if isinstance(key, JID):
|
|
||||||
key = key.bare
|
|
||||||
if key is None:
|
if key is None:
|
||||||
key = self.xmpp.boundjid.bare
|
key = self.xmpp.boundjid
|
||||||
|
if not isinstance(key, JID):
|
||||||
|
key = JID(key)
|
||||||
|
key = key.bare
|
||||||
|
|
||||||
if key not in self._rosters:
|
if key not in self._rosters:
|
||||||
self.add(key)
|
self.add(key)
|
||||||
self._rosters[key].auto_authorize = self.auto_authorize
|
self._rosters[key].auto_authorize = self.auto_authorize
|
||||||
@@ -90,8 +121,10 @@ class Roster(object):
|
|||||||
Arguments:
|
Arguments:
|
||||||
node -- The JID for the new roster node.
|
node -- The JID for the new roster node.
|
||||||
"""
|
"""
|
||||||
if isinstance(node, JID):
|
if not isinstance(node, JID):
|
||||||
node = node.bare
|
node = JID(node)
|
||||||
|
|
||||||
|
node = node.bare
|
||||||
if node not in self._rosters:
|
if node not in self._rosters:
|
||||||
self._rosters[node] = RosterNode(self.xmpp, node, self.db)
|
self._rosters[node] = RosterNode(self.xmpp, node, self.db)
|
||||||
|
|
||||||
@@ -121,29 +154,27 @@ class Roster(object):
|
|||||||
for node in self:
|
for node in self:
|
||||||
self[node].reset()
|
self[node].reset()
|
||||||
|
|
||||||
def send_presence(self, pshow=None, pstatus=None, ppriority=None,
|
def send_presence(self, **kwargs):
|
||||||
pto=None, pfrom=None, ptype=None, pnick=None):
|
|
||||||
"""
|
"""
|
||||||
Create, initialize, and send a Presence stanza.
|
Create, initialize, and send a Presence stanza.
|
||||||
|
|
||||||
Forwards the send request to the appropriate roster to
|
If no recipient is specified, send the presence immediately.
|
||||||
perform the actual sending.
|
Otherwise, forward the send request to the recipient's roster
|
||||||
|
entry for processing.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
pshow -- The presence's show value.
|
pshow -- The presence's show value.
|
||||||
pstatus -- The presence's status message.
|
pstatus -- The presence's status message.
|
||||||
ppriority -- This connections' priority.
|
ppriority -- This connections' priority.
|
||||||
pto -- The recipient of a directed presence.
|
pto -- The recipient of a directed presence.
|
||||||
|
pfrom -- The sender of a directed presence, which should
|
||||||
|
be the owner JID plus resource.
|
||||||
ptype -- The type of presence, such as 'subscribe'.
|
ptype -- The type of presence, such as 'subscribe'.
|
||||||
pfrom -- The sender of the presence.
|
|
||||||
pnick -- Optional nickname of the presence's sender.
|
pnick -- Optional nickname of the presence's sender.
|
||||||
"""
|
"""
|
||||||
self[pfrom].send_presence(ptype=ptype,
|
if self.xmpp.is_component and not kwargs.get('pfrom', ''):
|
||||||
pshow=pshow,
|
kwargs['pfrom'] = self.jid
|
||||||
pstatus=pstatus,
|
self.xmpp.send_presence(**kwargs)
|
||||||
ppriority=ppriority,
|
|
||||||
pnick=pnick,
|
|
||||||
pto=pto)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def auto_authorize(self):
|
def auto_authorize(self):
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
See the file LICENSE for copying permission.
|
See the file LICENSE for copying permission.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import threading
|
||||||
|
|
||||||
from sleekxmpp.xmlstream import JID
|
from sleekxmpp.xmlstream import JID
|
||||||
from sleekxmpp.roster import RosterItem
|
from sleekxmpp.roster import RosterItem
|
||||||
|
|
||||||
@@ -59,6 +61,7 @@ class RosterNode(object):
|
|||||||
self.last_status = None
|
self.last_status = None
|
||||||
self._version = ''
|
self._version = ''
|
||||||
self._jids = {}
|
self._jids = {}
|
||||||
|
self._last_status_lock = threading.Lock()
|
||||||
|
|
||||||
if self.db:
|
if self.db:
|
||||||
if hasattr(self.db, 'version'):
|
if hasattr(self.db, 'version'):
|
||||||
@@ -86,8 +89,11 @@ class RosterNode(object):
|
|||||||
|
|
||||||
A new item entry will be created if one does not already exist.
|
A new item entry will be created if one does not already exist.
|
||||||
"""
|
"""
|
||||||
if isinstance(key, JID):
|
if key is None:
|
||||||
key = key.bare
|
key = JID('')
|
||||||
|
if not isinstance(key, JID):
|
||||||
|
key = JID(key)
|
||||||
|
key = key.bare
|
||||||
if key not in self._jids:
|
if key not in self._jids:
|
||||||
self.add(key, save=True)
|
self.add(key, save=True)
|
||||||
return self._jids[key]
|
return self._jids[key]
|
||||||
@@ -98,8 +104,11 @@ class RosterNode(object):
|
|||||||
|
|
||||||
To remove an item from the server, use the remove() method.
|
To remove an item from the server, use the remove() method.
|
||||||
"""
|
"""
|
||||||
if isinstance(key, JID):
|
if key is None:
|
||||||
key = key.bare
|
key = JID('')
|
||||||
|
if not isinstance(key, JID):
|
||||||
|
key = JID(key)
|
||||||
|
key = key.bare
|
||||||
if key in self._jids:
|
if key in self._jids:
|
||||||
del self._jids[key]
|
del self._jids[key]
|
||||||
|
|
||||||
@@ -291,8 +300,7 @@ class RosterNode(object):
|
|||||||
for jid in self:
|
for jid in self:
|
||||||
self[jid].reset()
|
self[jid].reset()
|
||||||
|
|
||||||
def send_presence(self, ptype=None, pshow=None, pstatus=None,
|
def send_presence(self, **kwargs):
|
||||||
ppriority=None, pnick=None, pto=None):
|
|
||||||
"""
|
"""
|
||||||
Create, initialize, and send a Presence stanza.
|
Create, initialize, and send a Presence stanza.
|
||||||
|
|
||||||
@@ -305,27 +313,14 @@ class RosterNode(object):
|
|||||||
pstatus -- The presence's status message.
|
pstatus -- The presence's status message.
|
||||||
ppriority -- This connections' priority.
|
ppriority -- This connections' priority.
|
||||||
pto -- The recipient of a directed presence.
|
pto -- The recipient of a directed presence.
|
||||||
|
pfrom -- The sender of a directed presence, which should
|
||||||
|
be the owner JID plus resource.
|
||||||
ptype -- The type of presence, such as 'subscribe'.
|
ptype -- The type of presence, such as 'subscribe'.
|
||||||
|
pnick -- Optional nickname of the presence's sender.
|
||||||
"""
|
"""
|
||||||
if pto:
|
if self.xmpp.is_component and not kwargs.get('pfrom', ''):
|
||||||
self[pto].send_presence(ptype, pshow, pstatus,
|
kwargs['pfrom'] = self.jid
|
||||||
ppriority, pnick)
|
self.xmpp.send_presence(**kwargs)
|
||||||
else:
|
|
||||||
p = self.xmpp.make_presence(pshow=pshow,
|
|
||||||
pstatus=pstatus,
|
|
||||||
ppriority=ppriority,
|
|
||||||
ptype=ptype,
|
|
||||||
pnick=pnick)
|
|
||||||
if self.xmpp.is_component:
|
|
||||||
p['from'] = self.jid
|
|
||||||
if p['type'] in p.showtypes or \
|
|
||||||
p['type'] in ['available', 'unavailable']:
|
|
||||||
self.last_status = p
|
|
||||||
p.send()
|
|
||||||
|
|
||||||
if not self.xmpp.sentpresence:
|
|
||||||
self.xmpp.event('sent_presence')
|
|
||||||
self.xmpp.sentpresence = True
|
|
||||||
|
|
||||||
def send_last_presence(self):
|
def send_last_presence(self):
|
||||||
if self.last_status is None:
|
if self.last_status is None:
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ class Error(ElementBase):
|
|||||||
namespace = 'jabber:client'
|
namespace = 'jabber:client'
|
||||||
name = 'error'
|
name = 'error'
|
||||||
plugin_attrib = 'error'
|
plugin_attrib = 'error'
|
||||||
interfaces = set(('code', 'condition', 'text', 'type'))
|
interfaces = set(('code', 'condition', 'text', 'type',
|
||||||
|
'gone', 'redirect'))
|
||||||
sub_interfaces = set(('text',))
|
sub_interfaces = set(('text',))
|
||||||
plugin_attrib_map = {}
|
plugin_attrib_map = {}
|
||||||
plugin_tag_map = {}
|
plugin_tag_map = {}
|
||||||
@@ -88,7 +89,7 @@ class Error(ElementBase):
|
|||||||
|
|
||||||
def get_condition(self):
|
def get_condition(self):
|
||||||
"""Return the condition element's name."""
|
"""Return the condition element's name."""
|
||||||
for child in self.xml.getchildren():
|
for child in self.xml:
|
||||||
if "{%s}" % self.condition_ns in child.tag:
|
if "{%s}" % self.condition_ns in child.tag:
|
||||||
cond = child.tag.split('}', 1)[-1]
|
cond = child.tag.split('}', 1)[-1]
|
||||||
if cond in self.conditions:
|
if cond in self.conditions:
|
||||||
@@ -109,7 +110,7 @@ class Error(ElementBase):
|
|||||||
|
|
||||||
def del_condition(self):
|
def del_condition(self):
|
||||||
"""Remove the condition element."""
|
"""Remove the condition element."""
|
||||||
for child in self.xml.getchildren():
|
for child in self.xml:
|
||||||
if "{%s}" % self.condition_ns in child.tag:
|
if "{%s}" % self.condition_ns in child.tag:
|
||||||
tag = child.tag.split('}', 1)[-1]
|
tag = child.tag.split('}', 1)[-1]
|
||||||
if tag in self.conditions:
|
if tag in self.conditions:
|
||||||
@@ -135,6 +136,33 @@ class Error(ElementBase):
|
|||||||
self._del_sub('{%s}text' % self.condition_ns)
|
self._del_sub('{%s}text' % self.condition_ns)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def get_gone(self):
|
||||||
|
return self._get_sub_text('{%s}gone' % self.condition_ns, '')
|
||||||
|
|
||||||
|
def get_redirect(self):
|
||||||
|
return self._get_sub_text('{%s}redirect' % self.condition_ns, '')
|
||||||
|
|
||||||
|
def set_gone(self, value):
|
||||||
|
if value:
|
||||||
|
del self['condition']
|
||||||
|
return self._set_sub_text('{%s}gone' % self.condition_ns, value)
|
||||||
|
elif self['condition'] == 'gone':
|
||||||
|
del self['condition']
|
||||||
|
|
||||||
|
def set_redirect(self, value):
|
||||||
|
if value:
|
||||||
|
del self['condition']
|
||||||
|
ns = self.condition_ns
|
||||||
|
return self._set_sub_text('{%s}redirect' % ns, value)
|
||||||
|
elif self['condition'] == 'redirect':
|
||||||
|
del self['condition']
|
||||||
|
|
||||||
|
def del_gone(self):
|
||||||
|
self._del_sub('{%s}gone' % self.condition_ns)
|
||||||
|
|
||||||
|
def del_redirect(self):
|
||||||
|
self._del_sub('{%s}redirect' % self.condition_ns)
|
||||||
|
|
||||||
|
|
||||||
# To comply with PEP8, method names now use underscores.
|
# To comply with PEP8, method names now use underscores.
|
||||||
# Deprecated method names are re-mapped for backwards compatibility.
|
# Deprecated method names are re-mapped for backwards compatibility.
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class Iq(RootStanza):
|
|||||||
|
|
||||||
def get_query(self):
|
def get_query(self):
|
||||||
"""Return the namespace of the <query> element."""
|
"""Return the namespace of the <query> element."""
|
||||||
for child in self.xml.getchildren():
|
for child in self.xml:
|
||||||
if child.tag.endswith('query'):
|
if child.tag.endswith('query'):
|
||||||
ns = child.tag.split('}')[0]
|
ns = child.tag.split('}')[0]
|
||||||
if '{' in ns:
|
if '{' in ns:
|
||||||
@@ -132,7 +132,7 @@ class Iq(RootStanza):
|
|||||||
|
|
||||||
def del_query(self):
|
def del_query(self):
|
||||||
"""Remove the <query> element."""
|
"""Remove the <query> element."""
|
||||||
for child in self.xml.getchildren():
|
for child in self.xml:
|
||||||
if child.tag.endswith('query'):
|
if child.tag.endswith('query'):
|
||||||
self.xml.remove(child)
|
self.xml.remove(child)
|
||||||
return self
|
return self
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from sleekxmpp.stanza.rootstanza import RootStanza
|
from sleekxmpp.stanza.rootstanza import RootStanza
|
||||||
from sleekxmpp.xmlstream import StanzaBase
|
from sleekxmpp.xmlstream import StanzaBase, ET
|
||||||
|
|
||||||
|
|
||||||
class Message(RootStanza):
|
class Message(RootStanza):
|
||||||
@@ -54,13 +54,14 @@ class Message(RootStanza):
|
|||||||
del_mucnick -- Dummy method to prevent deletion.
|
del_mucnick -- Dummy method to prevent deletion.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
namespace = 'jabber:client'
|
|
||||||
name = 'message'
|
name = 'message'
|
||||||
interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject',
|
namespace = 'jabber:client'
|
||||||
'mucroom', 'mucnick'))
|
|
||||||
sub_interfaces = set(('body', 'subject'))
|
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
types = set((None, 'normal', 'chat', 'headline', 'error', 'groupchat'))
|
interfaces = set(['type', 'to', 'from', 'id', 'body', 'subject',
|
||||||
|
'thread', 'parent_thread', 'mucroom', 'mucnick'])
|
||||||
|
sub_interfaces = set(['body', 'subject', 'thread'])
|
||||||
|
lang_interfaces = sub_interfaces
|
||||||
|
types = set(['normal', 'chat', 'headline', 'error', 'groupchat'])
|
||||||
|
|
||||||
def get_type(self):
|
def get_type(self):
|
||||||
"""
|
"""
|
||||||
@@ -72,6 +73,31 @@ class Message(RootStanza):
|
|||||||
"""
|
"""
|
||||||
return self._get_attr('type', 'normal')
|
return self._get_attr('type', 'normal')
|
||||||
|
|
||||||
|
def get_parent_thread(self):
|
||||||
|
"""Return the message thread's parent thread."""
|
||||||
|
thread = self.xml.find('{%s}thread' % self.namespace)
|
||||||
|
if thread is not None:
|
||||||
|
return thread.attrib.get('parent', '')
|
||||||
|
return ''
|
||||||
|
|
||||||
|
def set_parent_thread(self, value):
|
||||||
|
"""Add or change the message thread's parent thread."""
|
||||||
|
thread = self.xml.find('{%s}thread' % self.namespace)
|
||||||
|
if value:
|
||||||
|
if thread is None:
|
||||||
|
thread = ET.Element('{%s}thread' % self.namespace)
|
||||||
|
self.xml.append(thread)
|
||||||
|
thread.attrib['parent'] = value
|
||||||
|
else:
|
||||||
|
if thread is not None and 'parent' in thread.attrib:
|
||||||
|
del thread.attrib['parent']
|
||||||
|
|
||||||
|
def del_parent_thread(self):
|
||||||
|
"""Delete the message thread's parent reference."""
|
||||||
|
thread = self.xml.find('{%s}thread' % self.namespace)
|
||||||
|
if thread is not None and 'parent' in thread.attrib:
|
||||||
|
del thread.attrib['parent']
|
||||||
|
|
||||||
def chat(self):
|
def chat(self):
|
||||||
"""Set the message type to 'chat'."""
|
"""Set the message type to 'chat'."""
|
||||||
self['type'] = 'chat'
|
self['type'] = 'chat'
|
||||||
@@ -96,10 +122,16 @@ class Message(RootStanza):
|
|||||||
clear -- Indicates if existing content should be removed
|
clear -- Indicates if existing content should be removed
|
||||||
before replying. Defaults to True.
|
before replying. Defaults to True.
|
||||||
"""
|
"""
|
||||||
|
thread = self['thread']
|
||||||
|
parent = self['parent_thread']
|
||||||
|
|
||||||
StanzaBase.reply(self, clear)
|
StanzaBase.reply(self, clear)
|
||||||
if self['type'] == 'groupchat':
|
if self['type'] == 'groupchat':
|
||||||
self['to'] = self['to'].bare
|
self['to'] = self['to'].bare
|
||||||
|
|
||||||
|
self['thread'] = thread
|
||||||
|
self['parent_thread'] = parent
|
||||||
|
|
||||||
del self['id']
|
del self['id']
|
||||||
|
|
||||||
if body is not None:
|
if body is not None:
|
||||||
|
|||||||
@@ -60,16 +60,17 @@ class Presence(RootStanza):
|
|||||||
set_priority -- Set the value of the <priority> element.
|
set_priority -- Set the value of the <priority> element.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
namespace = 'jabber:client'
|
|
||||||
name = 'presence'
|
name = 'presence'
|
||||||
interfaces = set(('type', 'to', 'from', 'id', 'show',
|
namespace = 'jabber:client'
|
||||||
'status', 'priority'))
|
|
||||||
sub_interfaces = set(('show', 'status', 'priority'))
|
|
||||||
plugin_attrib = name
|
plugin_attrib = name
|
||||||
|
interfaces = set(['type', 'to', 'from', 'id', 'show',
|
||||||
|
'status', 'priority'])
|
||||||
|
sub_interfaces = set(['show', 'status', 'priority'])
|
||||||
|
lang_interfaces = set(['status'])
|
||||||
|
|
||||||
types = set(('available', 'unavailable', 'error', 'probe', 'subscribe',
|
types = set(['available', 'unavailable', 'error', 'probe', 'subscribe',
|
||||||
'subscribed', 'unsubscribe', 'unsubscribed'))
|
'subscribed', 'unsubscribe', 'unsubscribed'])
|
||||||
showtypes = set(('dnd', 'chat', 'xa', 'away'))
|
showtypes = set(['dnd', 'chat', 'xa', 'away'])
|
||||||
|
|
||||||
def exception(self, e):
|
def exception(self, e):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ class RootStanza(StanzaBase):
|
|||||||
self['error']['type'] = 'cancel'
|
self['error']['type'] = 'cancel'
|
||||||
self.send()
|
self.send()
|
||||||
# log the error
|
# log the error
|
||||||
log.exception('Error handling {%s}%s stanza' , self.namespace, self.name)
|
log.exception('Error handling {%s}%s stanza',
|
||||||
|
self.namespace, self.name)
|
||||||
# Finally raise the exception to a global exception handler
|
# Finally raise the exception to a global exception handler
|
||||||
self.stream.exception(e)
|
self.stream.exception(e)
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ class Roster(ElementBase):
|
|||||||
# Remove extra JID reference to keep everything
|
# Remove extra JID reference to keep everything
|
||||||
# backward compatible
|
# backward compatible
|
||||||
del items[item['jid']]['jid']
|
del items[item['jid']]['jid']
|
||||||
|
del items[item['jid']]['lang']
|
||||||
return items
|
return items
|
||||||
|
|
||||||
def del_items(self):
|
def del_items(self):
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class StreamError(Error, StanzaBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
namespace = 'http://etherx.jabber.org/streams'
|
namespace = 'http://etherx.jabber.org/streams'
|
||||||
interfaces = set(('condition', 'text'))
|
interfaces = set(('condition', 'text', 'see_other_host'))
|
||||||
conditions = set((
|
conditions = set((
|
||||||
'bad-format', 'bad-namespace-prefix', 'conflict',
|
'bad-format', 'bad-namespace-prefix', 'conflict',
|
||||||
'connection-timeout', 'host-gone', 'host-unknown',
|
'connection-timeout', 'host-gone', 'host-unknown',
|
||||||
@@ -66,3 +66,18 @@ class StreamError(Error, StanzaBase):
|
|||||||
'unsupported-feature', 'unsupported-stanza-type',
|
'unsupported-feature', 'unsupported-stanza-type',
|
||||||
'unsupported-version'))
|
'unsupported-version'))
|
||||||
condition_ns = 'urn:ietf:params:xml:ns:xmpp-streams'
|
condition_ns = 'urn:ietf:params:xml:ns:xmpp-streams'
|
||||||
|
|
||||||
|
def get_see_other_host(self):
|
||||||
|
ns = self.condition_ns
|
||||||
|
return self._get_sub_text('{%s}see-other-host' % ns, '')
|
||||||
|
|
||||||
|
def set_see_other_host(self, value):
|
||||||
|
if value:
|
||||||
|
del self['condition']
|
||||||
|
ns = self.condition_ns
|
||||||
|
return self._set_sub_text('{%s}see-other-host' % ns, value)
|
||||||
|
elif self['condition'] == 'see-other-host':
|
||||||
|
del self['condition']
|
||||||
|
|
||||||
|
def del_see_other_host(self):
|
||||||
|
self._del_sub('{%s}see-other-host' % self.condition_ns)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
See the file LICENSE for copying permission.
|
See the file LICENSE for copying permission.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from sleekxmpp.thirdparty import OrderedDict
|
||||||
from sleekxmpp.xmlstream import StanzaBase
|
from sleekxmpp.xmlstream import StanzaBase
|
||||||
|
|
||||||
|
|
||||||
@@ -28,7 +29,10 @@ class StreamFeatures(StanzaBase):
|
|||||||
def get_features(self):
|
def get_features(self):
|
||||||
"""
|
"""
|
||||||
"""
|
"""
|
||||||
return self.plugins
|
features = OrderedDict()
|
||||||
|
for (name, lang), plugin in self.plugins.items():
|
||||||
|
features[name] = plugin
|
||||||
|
return features
|
||||||
|
|
||||||
def set_features(self, value):
|
def set_features(self, value):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class SleekTest(unittest.TestCase):
|
|||||||
known_prefixes[prefix],
|
known_prefixes[prefix],
|
||||||
xml_string)
|
xml_string)
|
||||||
xml = self.parse_xml(xml_string)
|
xml = self.parse_xml(xml_string)
|
||||||
xml = xml.getchildren()[0]
|
xml = list(xml)[0]
|
||||||
return xml
|
return xml
|
||||||
else:
|
else:
|
||||||
self.fail("XML data was mal-formed:\n%s" % xml_string)
|
self.fail("XML data was mal-formed:\n%s" % xml_string)
|
||||||
@@ -333,6 +333,8 @@ class SleekTest(unittest.TestCase):
|
|||||||
# Remove unique ID prefix to make it easier to test
|
# Remove unique ID prefix to make it easier to test
|
||||||
self.xmpp._id_prefix = ''
|
self.xmpp._id_prefix = ''
|
||||||
self.xmpp._disconnect_wait_for_threads = False
|
self.xmpp._disconnect_wait_for_threads = False
|
||||||
|
self.xmpp.default_lang = None
|
||||||
|
self.xmpp.peer_default_lang = None
|
||||||
|
|
||||||
# We will use this to wait for the session_start event
|
# We will use this to wait for the session_start event
|
||||||
# for live connections.
|
# for live connections.
|
||||||
@@ -386,6 +388,7 @@ class SleekTest(unittest.TestCase):
|
|||||||
sid='',
|
sid='',
|
||||||
stream_ns="http://etherx.jabber.org/streams",
|
stream_ns="http://etherx.jabber.org/streams",
|
||||||
default_ns="jabber:client",
|
default_ns="jabber:client",
|
||||||
|
default_lang="en",
|
||||||
version="1.0",
|
version="1.0",
|
||||||
xml_header=True):
|
xml_header=True):
|
||||||
"""
|
"""
|
||||||
@@ -413,6 +416,8 @@ class SleekTest(unittest.TestCase):
|
|||||||
parts.append('from="%s"' % sfrom)
|
parts.append('from="%s"' % sfrom)
|
||||||
if sid:
|
if sid:
|
||||||
parts.append('id="%s"' % sid)
|
parts.append('id="%s"' % sid)
|
||||||
|
if default_lang:
|
||||||
|
parts.append('xml:lang="%s"' % default_lang)
|
||||||
parts.append('version="%s"' % version)
|
parts.append('version="%s"' % version)
|
||||||
parts.append('xmlns:stream="%s"' % stream_ns)
|
parts.append('xmlns:stream="%s"' % stream_ns)
|
||||||
parts.append('xmlns="%s"' % default_ns)
|
parts.append('xmlns="%s"' % default_ns)
|
||||||
@@ -512,9 +517,9 @@ class SleekTest(unittest.TestCase):
|
|||||||
if '{%s}lang' % xml_ns in recv_xml.attrib:
|
if '{%s}lang' % xml_ns in recv_xml.attrib:
|
||||||
del recv_xml.attrib['{%s}lang' % xml_ns]
|
del recv_xml.attrib['{%s}lang' % xml_ns]
|
||||||
|
|
||||||
if recv_xml.getchildren:
|
if list(recv_xml):
|
||||||
# We received more than just the header
|
# We received more than just the header
|
||||||
for xml in recv_xml.getchildren():
|
for xml in recv_xml:
|
||||||
self.xmpp.socket.recv_data(tostring(xml))
|
self.xmpp.socket.recv_data(tostring(xml))
|
||||||
|
|
||||||
attrib = recv_xml.attrib
|
attrib = recv_xml.attrib
|
||||||
@@ -564,6 +569,7 @@ class SleekTest(unittest.TestCase):
|
|||||||
sid='',
|
sid='',
|
||||||
stream_ns="http://etherx.jabber.org/streams",
|
stream_ns="http://etherx.jabber.org/streams",
|
||||||
default_ns="jabber:client",
|
default_ns="jabber:client",
|
||||||
|
default_lang="en",
|
||||||
version="1.0",
|
version="1.0",
|
||||||
xml_header=False,
|
xml_header=False,
|
||||||
timeout=1):
|
timeout=1):
|
||||||
@@ -585,6 +591,7 @@ class SleekTest(unittest.TestCase):
|
|||||||
header = self.make_header(sto, sfrom, sid,
|
header = self.make_header(sto, sfrom, sid,
|
||||||
stream_ns=stream_ns,
|
stream_ns=stream_ns,
|
||||||
default_ns=default_ns,
|
default_ns=default_ns,
|
||||||
|
default_lang=default_lang,
|
||||||
version=version,
|
version=version,
|
||||||
xml_header=xml_header)
|
xml_header=xml_header)
|
||||||
sent_header = self.xmpp.socket.next_sent(timeout)
|
sent_header = self.xmpp.socket.next_sent(timeout)
|
||||||
@@ -691,7 +698,7 @@ class SleekTest(unittest.TestCase):
|
|||||||
if xml.tag.startswith('{'):
|
if xml.tag.startswith('{'):
|
||||||
return
|
return
|
||||||
xml.tag = '{%s}%s' % (ns, xml.tag)
|
xml.tag = '{%s}%s' % (ns, xml.tag)
|
||||||
for child in xml.getchildren():
|
for child in xml:
|
||||||
self.fix_namespaces(child, ns)
|
self.fix_namespaces(child, ns)
|
||||||
|
|
||||||
def compare(self, xml, *other):
|
def compare(self, xml, *other):
|
||||||
@@ -734,7 +741,7 @@ class SleekTest(unittest.TestCase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# Step 4: Check children count
|
# Step 4: Check children count
|
||||||
if len(xml.getchildren()) != len(other.getchildren()):
|
if len(list(xml)) != len(list(other)):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Step 5: Recursively check children
|
# Step 5: Recursively check children
|
||||||
|
|||||||
3
sleekxmpp/thirdparty/suelta/util.py
vendored
3
sleekxmpp/thirdparty/suelta/util.py
vendored
@@ -15,6 +15,9 @@ def bytes(text):
|
|||||||
:param text: Unicode text to convert to bytes
|
:param text: Unicode text to convert to bytes
|
||||||
:rtype: bytes (Python3), str (Python2.6+)
|
:rtype: bytes (Python3), str (Python2.6+)
|
||||||
"""
|
"""
|
||||||
|
if text is None:
|
||||||
|
return b''
|
||||||
|
|
||||||
if sys.version_info < (3, 0):
|
if sys.version_info < (3, 0):
|
||||||
import __builtin__
|
import __builtin__
|
||||||
return __builtin__.bytes(text)
|
return __builtin__.bytes(text)
|
||||||
|
|||||||
@@ -9,5 +9,5 @@
|
|||||||
# We don't want to have to import the entire library
|
# We don't want to have to import the entire library
|
||||||
# just to get the version info for setup.py
|
# just to get the version info for setup.py
|
||||||
|
|
||||||
__version__ = '1.1.3'
|
__version__ = '1.1.9'
|
||||||
__version_info__ = (1, 1, 3, '', 0)
|
__version_info__ = (1, 1, 9, '', 0)
|
||||||
|
|||||||
@@ -7,11 +7,12 @@ try:
|
|||||||
from pyasn1.type.univ import Any, ObjectIdentifier, OctetString
|
from pyasn1.type.univ import Any, ObjectIdentifier, OctetString
|
||||||
from pyasn1.type.char import BMPString, IA5String, UTF8String
|
from pyasn1.type.char import BMPString, IA5String, UTF8String
|
||||||
from pyasn1.type.useful import GeneralizedTime
|
from pyasn1.type.useful import GeneralizedTime
|
||||||
from pyasn1_modules.rfc2459 import Certificate, DirectoryString, SubjectAltName, GeneralNames, GeneralName
|
from pyasn1_modules.rfc2459 import (Certificate, DirectoryString,
|
||||||
|
SubjectAltName, GeneralNames,
|
||||||
|
GeneralName)
|
||||||
from pyasn1_modules.rfc2459 import id_ce_subjectAltName as SUBJECT_ALT_NAME
|
from pyasn1_modules.rfc2459 import id_ce_subjectAltName as SUBJECT_ALT_NAME
|
||||||
from pyasn1_modules.rfc2459 import id_at_commonName as COMMON_NAME
|
from pyasn1_modules.rfc2459 import id_at_commonName as COMMON_NAME
|
||||||
|
|
||||||
|
|
||||||
XMPP_ADDR = ObjectIdentifier('1.3.6.1.5.5.7.8.5')
|
XMPP_ADDR = ObjectIdentifier('1.3.6.1.5.5.7.8.5')
|
||||||
SRV_NAME = ObjectIdentifier('1.3.6.1.5.5.7.8.7')
|
SRV_NAME = ObjectIdentifier('1.3.6.1.5.5.7.8.7')
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ def extract_names(raw_cert):
|
|||||||
cert = decoder.decode(raw_cert, asn1Spec=Certificate())[0]
|
cert = decoder.decode(raw_cert, asn1Spec=Certificate())[0]
|
||||||
tbs = cert.getComponentByName('tbsCertificate')
|
tbs = cert.getComponentByName('tbsCertificate')
|
||||||
subject = tbs.getComponentByName('subject')
|
subject = tbs.getComponentByName('subject')
|
||||||
extensions = tbs.getComponentByName('extensions')
|
extensions = tbs.getComponentByName('extensions') or []
|
||||||
|
|
||||||
# Extract the CommonName(s) from the cert.
|
# Extract the CommonName(s) from the cert.
|
||||||
for rdnss in subject:
|
for rdnss in subject:
|
||||||
|
|||||||
@@ -143,3 +143,6 @@ class JID(object):
|
|||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
"""Hash a JID based on the string version of its full JID."""
|
"""Hash a JID based on the string version of its full JID."""
|
||||||
return hash(self.full)
|
return hash(self.full)
|
||||||
|
|
||||||
|
def __copy__(self):
|
||||||
|
return JID(self.jid)
|
||||||
|
|||||||
@@ -151,8 +151,8 @@ class MatchXMLMask(MatcherBase):
|
|||||||
"""
|
"""
|
||||||
tag = tag.split('}')[-1]
|
tag = tag.split('}')[-1]
|
||||||
try:
|
try:
|
||||||
children = [c.tag.split('}')[-1] for c in xml.getchildren()]
|
children = [c.tag.split('}')[-1] for c in xml]
|
||||||
index = children.index(tag)
|
index = children.index(tag)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return None
|
return None
|
||||||
return xml.getchildren()[index]
|
return list(xml)[index]
|
||||||
|
|||||||
@@ -77,10 +77,10 @@ class MatchXPath(MatcherBase):
|
|||||||
# Skip empty tag name artifacts from the cleanup phase.
|
# Skip empty tag name artifacts from the cleanup phase.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
children = [c.tag.split('}')[-1] for c in xml.getchildren()]
|
children = [c.tag.split('}')[-1] for c in xml]
|
||||||
try:
|
try:
|
||||||
index = children.index(tag)
|
index = children.index(tag)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return False
|
return False
|
||||||
xml = xml.getchildren()[index]
|
xml = list(xml)[index]
|
||||||
return True
|
return True
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user