mirror of
https://github.com/correl/SleekXMPP.git
synced 2024-11-27 19:19:54 +00:00
Merge branch 'develop' into roster
This commit is contained in:
commit
adade2e5ec
17 changed files with 966 additions and 424 deletions
198
examples/disco_browser.py
Executable file
198
examples/disco_browser.py
Executable file
|
@ -0,0 +1,198 @@
|
|||
#!/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 time
|
||||
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')
|
||||
|
||||
|
||||
class Disco(sleekxmpp.ClientXMPP):
|
||||
|
||||
"""
|
||||
A demonstration for using basic service discovery.
|
||||
|
||||
Send a disco#info and disco#items request to a JID/node combination,
|
||||
and print out the results.
|
||||
|
||||
May also request only particular info categories such as just features,
|
||||
or just items.
|
||||
"""
|
||||
|
||||
def __init__(self, jid, password, target_jid, target_node='', get=''):
|
||||
sleekxmpp.ClientXMPP.__init__(self, jid, password)
|
||||
|
||||
# Using service discovery requires the XEP-0030 plugin.
|
||||
self.register_plugin('xep_0030')
|
||||
|
||||
self.get = get
|
||||
self.target_jid = target_jid
|
||||
self.target_node = target_node
|
||||
|
||||
# Values to control which disco entities are reported
|
||||
self.info_types = ['', 'all', 'info', 'identities', 'features']
|
||||
self.identity_types = ['', 'all', 'info', 'identities']
|
||||
self.feature_types = ['', 'all', 'info', 'features']
|
||||
self.items_types = ['', 'all', 'items']
|
||||
|
||||
|
||||
# The session_start event will be triggered when
|
||||
# the bot establishes its connection with the server
|
||||
# and the XML streams are ready for use. We want to
|
||||
# listen for this event so that we we can intialize
|
||||
# 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 intial
|
||||
presence stanza.
|
||||
|
||||
In this case, we send disco#info and disco#items
|
||||
stanzas to the requested JID and print the results.
|
||||
|
||||
Arguments:
|
||||
event -- An empty dictionary. The session_start
|
||||
event does not provide any additional
|
||||
data.
|
||||
"""
|
||||
self.get_roster()
|
||||
self.send_presence()
|
||||
|
||||
if self.get in self.info_types:
|
||||
# By using block=True, the result stanza will be
|
||||
# returned. Execution will block until the reply is
|
||||
# received. Non-blocking options would be to listen
|
||||
# for the disco_info event, or passing a handler
|
||||
# function using the callback parameter.
|
||||
info = self['xep_0030'].get_info(jid=self.target_jid,
|
||||
node=self.target_node,
|
||||
block=True)
|
||||
if self.get in self.items_types:
|
||||
# The same applies from above. Listen for the
|
||||
# disco_items event or pass a callback function
|
||||
# if you need to process a non-blocking request.
|
||||
items = self['xep_0030'].get_items(jid=self.target_jid,
|
||||
node=self.target_node,
|
||||
block=True)
|
||||
else:
|
||||
logging.error("Invalid disco request type.")
|
||||
self.disconnect()
|
||||
return
|
||||
|
||||
header = 'XMPP Service Discovery: %s' % self.target_jid
|
||||
print(header)
|
||||
print('-' * len(header))
|
||||
if self.target_node != '':
|
||||
print('Node: %s' % self.target_node)
|
||||
print('-' * len(header))
|
||||
|
||||
if self.get in self.identity_types:
|
||||
print('Identities:')
|
||||
for identity in info['disco_info']['identities']:
|
||||
print(' - ', identity)
|
||||
|
||||
if self.get in self.feature_types:
|
||||
print('Features:')
|
||||
for feature in info['disco_info']['features']:
|
||||
print(' - %s' % feature)
|
||||
|
||||
if self.get in self.items_types:
|
||||
print('Items:')
|
||||
for item in items['disco_items']['items']:
|
||||
print(' - %s' % str(item))
|
||||
|
||||
self.disconnect()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Setup the command line arguments.
|
||||
optp = OptionParser()
|
||||
optp.version = '%%prog 0.1'
|
||||
optp.usage = "Usage: %%prog [options] %s <jid> [<node>]" % \
|
||||
'all|info|items|identities|features'
|
||||
|
||||
optp.add_option('-q','--quiet', help='set logging to ERROR',
|
||||
action='store_const',
|
||||
dest='loglevel',
|
||||
const=logging.ERROR,
|
||||
default=logging.ERROR)
|
||||
optp.add_option('-d','--debug', help='set logging to DEBUG',
|
||||
action='store_const',
|
||||
dest='loglevel',
|
||||
const=logging.DEBUG,
|
||||
default=logging.ERROR)
|
||||
optp.add_option('-v','--verbose', help='set logging to COMM',
|
||||
action='store_const',
|
||||
dest='loglevel',
|
||||
const=5,
|
||||
default=logging.ERROR)
|
||||
|
||||
# JID and password options.
|
||||
optp.add_option("-j", "--jid", dest="jid",
|
||||
help="JID to use")
|
||||
optp.add_option("-p", "--password", dest="password",
|
||||
help="password to use")
|
||||
opts,args = optp.parse_args()
|
||||
|
||||
# Setup logging.
|
||||
logging.basicConfig(level=opts.loglevel,
|
||||
format='%(levelname)-8s %(message)s')
|
||||
|
||||
if len(args) < 2:
|
||||
optp.print_help()
|
||||
exit()
|
||||
|
||||
if len(args) == 2:
|
||||
args = (args[0], args[1], '')
|
||||
|
||||
if opts.jid is None:
|
||||
opts.jid = raw_input("Username: ")
|
||||
if opts.password is None:
|
||||
opts.password = getpass.getpass("Password: ")
|
||||
|
||||
# Setup the Disco browser.
|
||||
xmpp = Disco(opts.jid, opts.password, args[1], args[2], args[0])
|
||||
|
||||
# If you are working with an OpenFire server, you may need
|
||||
# to adjust the SSL version used:
|
||||
# xmpp.ssl_version = ssl.PROTOCOL_SSLv3
|
||||
|
||||
# If you want to verify the SSL certificates offered by a server:
|
||||
# xmpp.ca_certs = "path/to/ca/cert"
|
||||
|
||||
# Connect to the XMPP server and start processing XMPP stanzas.
|
||||
if xmpp.connect():
|
||||
# If you do not have the pydns 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(threaded=False)
|
||||
else:
|
||||
print("Unable to connect.")
|
|
@ -118,6 +118,13 @@ if __name__ == '__main__':
|
|||
xmpp.registerPlugin('xep_0060') # PubSub
|
||||
xmpp.registerPlugin('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 pydns library installed, you will need
|
||||
|
|
4
setup.py
4
setup.py
|
@ -38,13 +38,15 @@ CLASSIFIERS = [ 'Intended Audience :: Developers',
|
|||
]
|
||||
|
||||
packages = [ 'sleekxmpp',
|
||||
'sleekxmpp/plugins',
|
||||
'sleekxmpp/stanza',
|
||||
'sleekxmpp/test',
|
||||
'sleekxmpp/xmlstream',
|
||||
'sleekxmpp/xmlstream/matcher',
|
||||
'sleekxmpp/xmlstream/handler',
|
||||
'sleekxmpp/thirdparty',
|
||||
'sleekxmpp/plugins',
|
||||
'sleekxmpp/plugins/xep_0030',
|
||||
'sleekxmpp/plugins/xep_0030/stanza'
|
||||
]
|
||||
|
||||
if sys.version_info < (3, 0):
|
||||
|
|
|
@ -271,7 +271,7 @@ class BaseXMPP(XMLStream):
|
|||
"""Create a Presence stanza associated with this stream."""
|
||||
return Presence(self, *args, **kwargs)
|
||||
|
||||
def make_iq(self, id=0, ifrom=None):
|
||||
def make_iq(self, id=0, ifrom=None, ito=None, type=None, query=None):
|
||||
"""
|
||||
Create a new Iq stanza with a given Id and from JID.
|
||||
|
||||
|
@ -279,11 +279,19 @@ class BaseXMPP(XMLStream):
|
|||
id -- An ideally unique ID value for this stanza thread.
|
||||
Defaults to 0.
|
||||
ifrom -- The from JID to use for this stanza.
|
||||
ito -- The destination JID for this stanza.
|
||||
type -- The Iq's type, one of: get, set, result, or error.
|
||||
query -- Optional namespace for adding a query element.
|
||||
"""
|
||||
return self.Iq()._set_stanza_values({'id': str(id),
|
||||
'from': ifrom})
|
||||
iq = self.Iq()
|
||||
iq['id'] = str(id)
|
||||
iq['to'] = ito
|
||||
iq['from'] = ifrom
|
||||
iq['type'] = itype
|
||||
iq['query'] = query
|
||||
return iq
|
||||
|
||||
def make_iq_get(self, queryxmlns=None):
|
||||
def make_iq_get(self, queryxmlns=None, ito=None, ifrom=None, iq=None):
|
||||
"""
|
||||
Create an Iq stanza of type 'get'.
|
||||
|
||||
|
@ -291,21 +299,45 @@ class BaseXMPP(XMLStream):
|
|||
|
||||
Arguments:
|
||||
queryxmlns -- The namespace of the query to use.
|
||||
ito -- The destination JID for this stanza.
|
||||
ifrom -- The from JID to use for this stanza.
|
||||
iq -- Optionally use an existing stanza instead
|
||||
of generating a new one.
|
||||
"""
|
||||
return self.Iq()._set_stanza_values({'type': 'get',
|
||||
'query': queryxmlns})
|
||||
if not iq:
|
||||
iq = self.Iq()
|
||||
iq['type'] = 'get'
|
||||
iq['query'] = queryxmlns
|
||||
if ito:
|
||||
iq['to'] = ito
|
||||
if ifrom:
|
||||
iq['from'] = ifrom
|
||||
return iq
|
||||
|
||||
def make_iq_result(self, id):
|
||||
def make_iq_result(self, id=None, ito=None, ifrom=None, iq=None):
|
||||
"""
|
||||
Create an Iq stanza of type 'result' with the given ID value.
|
||||
|
||||
Arguments:
|
||||
id -- An ideally unique ID value. May use self.new_id().
|
||||
id -- An ideally unique ID value. May use self.new_id().
|
||||
ito -- The destination JID for this stanza.
|
||||
ifrom -- The from JID to use for this stanza.
|
||||
iq -- Optionally use an existing stanza instead
|
||||
of generating a new one.
|
||||
"""
|
||||
return self.Iq()._set_stanza_values({'id': id,
|
||||
'type': 'result'})
|
||||
if not iq:
|
||||
iq = self.Iq()
|
||||
if id is None:
|
||||
id = self.new_id()
|
||||
iq['id'] = id
|
||||
iq['type'] = 'result'
|
||||
if ito:
|
||||
iq['to'] = ito
|
||||
if ifrom:
|
||||
iq['from'] = ifrom
|
||||
return iq
|
||||
|
||||
def make_iq_set(self, sub=None):
|
||||
def make_iq_set(self, sub=None, ito=None, ifrom=None, iq=None):
|
||||
"""
|
||||
Create an Iq stanza of type 'set'.
|
||||
|
||||
|
@ -313,15 +345,26 @@ class BaseXMPP(XMLStream):
|
|||
stanza's payload.
|
||||
|
||||
Arguments:
|
||||
sub -- A stanza or XML object to use as the Iq's payload.
|
||||
sub -- A stanza or XML object to use as the Iq's payload.
|
||||
ito -- The destination JID for this stanza.
|
||||
ifrom -- The from JID to use for this stanza.
|
||||
iq -- Optionally use an existing stanza instead
|
||||
of generating a new one.
|
||||
"""
|
||||
iq = self.Iq()._set_stanza_values({'type': 'set'})
|
||||
if not iq:
|
||||
iq = self.Iq()
|
||||
iq['type'] = 'set'
|
||||
if sub != None:
|
||||
iq.append(sub)
|
||||
if ito:
|
||||
iq['to'] = ito
|
||||
if ifrom:
|
||||
iq['from'] = ifrom
|
||||
return iq
|
||||
|
||||
def make_iq_error(self, id, type='cancel',
|
||||
condition='feature-not-implemented', text=None):
|
||||
condition='feature-not-implemented',
|
||||
text=None, ito=None, ifrom=None, iq=None):
|
||||
"""
|
||||
Create an Iq stanza of type 'error'.
|
||||
|
||||
|
@ -332,14 +375,24 @@ class BaseXMPP(XMLStream):
|
|||
condition -- The error condition.
|
||||
Defaults to 'feature-not-implemented'.
|
||||
text -- A message describing the cause of the error.
|
||||
ito -- The destination JID for this stanza.
|
||||
ifrom -- The from JID to use for this stanza.
|
||||
iq -- Optionally use an existing stanza instead
|
||||
of generating a new one.
|
||||
"""
|
||||
iq = self.Iq()._set_stanza_values({'id': id})
|
||||
iq['error']._set_stanza_values({'type': type,
|
||||
'condition': condition,
|
||||
'text': text})
|
||||
if not iq:
|
||||
iq = self.Iq()
|
||||
iq['id'] = id
|
||||
iq['error']['type'] = type
|
||||
iq['error']['condition'] = condition
|
||||
iq['error']['text'] = text
|
||||
if ito:
|
||||
iq['to'] = ito
|
||||
if ifrom:
|
||||
iq['from'] = ifrom
|
||||
return iq
|
||||
|
||||
def make_iq_query(self, iq=None, xmlns=''):
|
||||
def make_iq_query(self, iq=None, xmlns='', ito=None, ifrom=None):
|
||||
"""
|
||||
Create or modify an Iq stanza to use the given
|
||||
query namespace.
|
||||
|
@ -348,10 +401,16 @@ class BaseXMPP(XMLStream):
|
|||
iq -- Optional Iq stanza to modify. A new
|
||||
stanza is created otherwise.
|
||||
xmlns -- The query's namespace.
|
||||
ito -- The destination JID for this stanza.
|
||||
ifrom -- The from JID to use for this stanza.
|
||||
"""
|
||||
if not iq:
|
||||
iq = self.Iq()
|
||||
iq['query'] = xmlns
|
||||
if ito:
|
||||
iq['to'] = ito
|
||||
if ifrom:
|
||||
iq['from'] = ifrom
|
||||
return iq
|
||||
|
||||
def make_query_roster(self, iq=None):
|
||||
|
|
|
@ -5,6 +5,6 @@
|
|||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
__all__ = ['xep_0004', 'xep_0012', 'xep_0030', 'xep_0033', 'xep_0045',
|
||||
'xep_0050', 'xep_0085', 'xep_0092', 'xep_0199', 'gmail_notify',
|
||||
'xep_0060', 'xep_0202']
|
||||
__all__ = ['xep_0004', 'xep_0009', 'xep_0012', 'xep_0030', 'xep_0033',
|
||||
'xep_0045', 'xep_0050', 'xep_0060', 'xep_0085', 'xep_0086',
|
||||
'xep_0092', 'xep_0128', 'xep_0199', 'xep_0202', 'gmail_notify']
|
||||
|
|
|
@ -143,7 +143,7 @@ class gmail_notify(base.base_plugin):
|
|||
log.info('Gmail: Searching for emails matching: "%s"' % query)
|
||||
iq = self.xmpp.Iq()
|
||||
iq['type'] = 'get'
|
||||
iq['to'] = self.xmpp.jid
|
||||
iq['to'] = self.xmpp.boundjid.bare
|
||||
iq['gmail']['q'] = query
|
||||
iq['gmail']['newer-than-time'] = newer
|
||||
return iq.send()
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from . import base
|
||||
import logging
|
||||
from xml.etree import cElementTree as ET
|
||||
import types
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -43,7 +42,7 @@ class jobs(base.base_plugin):
|
|||
iq['psstate']['item'] = jobid
|
||||
iq['psstate']['payload'] = state
|
||||
result = iq.send()
|
||||
if result is None or type(result) == types.BooleanType or result['type'] != 'result':
|
||||
if result is None or type(result) == bool or result['type'] != 'result':
|
||||
log.error("Unable to change %s:%s to %s" % (node, jobid, state))
|
||||
return False
|
||||
return True
|
||||
|
|
|
@ -13,7 +13,6 @@ from .. xmlstream.handler.callback import Callback
|
|||
from .. xmlstream.matcher.xpath import MatchXPath
|
||||
from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID
|
||||
from .. stanza.message import Message
|
||||
import types
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -203,7 +202,7 @@ class Form(ElementBase):
|
|||
|
||||
def merge(self, other):
|
||||
new = copy.copy(self)
|
||||
if type(other) == types.DictType:
|
||||
if type(other) == dict:
|
||||
new.setValues(other)
|
||||
return new
|
||||
nfields = new.getFields(use_dict=True)
|
||||
|
|
|
@ -26,30 +26,66 @@ class xep_0030(base_plugin):
|
|||
"""
|
||||
XEP-0030: Service Discovery
|
||||
|
||||
Service discovery in XMPP allows entities to discover information about
|
||||
other agents in the network, such as the feature sets supported by a
|
||||
client, or signposts to other, related entities.
|
||||
|
||||
Also see <http://www.xmpp.org/extensions/xep-0030.html>.
|
||||
|
||||
The XEP-0030 plugin works using a hierarchy of dynamic
|
||||
node handlers, ranging from global handlers to specific
|
||||
JID+node handlers. The default set of handlers operate
|
||||
in a static manner, storing disco information in memory.
|
||||
However, custom handlers may use any available backend
|
||||
storage mechanism desired, such as SQLite or Redis.
|
||||
|
||||
Node handler hierarchy:
|
||||
JID | Node | Level
|
||||
---------------------
|
||||
None | None | Global
|
||||
Given | None | All nodes for the JID
|
||||
None | Given | Node on self.xmpp.boundjid
|
||||
Given | Given | A single node
|
||||
|
||||
Stream Handlers:
|
||||
Disco Info --
|
||||
Disco Items --
|
||||
Disco Info -- Any Iq stanze that includes a query with the
|
||||
namespace http://jabber.org/protocol/disco#info.
|
||||
Disco Items -- Any Iq stanze that includes a query with the
|
||||
namespace http://jabber.org/protocol/disco#items.
|
||||
|
||||
Events:
|
||||
disco_info --
|
||||
disco_items --
|
||||
disco_info_query --
|
||||
disco_items_query --
|
||||
disco_info -- Received a disco#info Iq query result.
|
||||
disco_items -- Received a disco#items Iq query result.
|
||||
disco_info_query -- Received a disco#info Iq query request.
|
||||
disco_items_query -- Received a disco#items Iq query request.
|
||||
|
||||
Attributes:
|
||||
stanza -- A reference to the module containing the stanza classes
|
||||
provided by this plugin.
|
||||
static -- Object containing the default set of static node handlers.
|
||||
xmpp -- The main SleekXMPP object.
|
||||
|
||||
Methods:
|
||||
set_node_handler --
|
||||
del_node_handler --
|
||||
add_identity --
|
||||
set_node_handler -- Assign a handler to a JID/node combination.
|
||||
del_node_handler -- Remove a handler from a JID/node combination.
|
||||
get_info -- Retrieve disco#info data, locally or remote.
|
||||
get_items -- Retrieve disco#items data, locally or remote.
|
||||
set_identities --
|
||||
set_features --
|
||||
set_items --
|
||||
del_items --
|
||||
del_identity --
|
||||
add_feature --
|
||||
del_feature --
|
||||
add_item --
|
||||
del_item --
|
||||
get_info --
|
||||
get_items --
|
||||
add_identity --
|
||||
add_feature --
|
||||
add_item --
|
||||
"""
|
||||
|
||||
def plugin_init(self):
|
||||
"""
|
||||
Start the XEP-0030 plugin.
|
||||
"""
|
||||
self.xep = '0030'
|
||||
self.description = 'Service Discovery'
|
||||
self.stanza = sleekxmpp.plugins.xep_0030.stanza
|
||||
|
@ -70,42 +106,89 @@ class xep_0030(base_plugin):
|
|||
self.static = StaticDisco(self.xmpp)
|
||||
|
||||
self._disco_ops = ['get_info', 'set_identities', 'set_features',
|
||||
'del_info', 'get_items', 'set_items', 'del_items',
|
||||
'get_items', 'set_items', 'del_items',
|
||||
'add_identity', 'del_identity', 'add_feature',
|
||||
'del_feature', 'add_item', 'del_item']
|
||||
self.handlers = {}
|
||||
self._handlers = {}
|
||||
for op in self._disco_ops:
|
||||
self.handlers[op] = {'global': getattr(self.static, op),
|
||||
'jid': {},
|
||||
'node': {}}
|
||||
|
||||
self._handlers[op] = {'global': getattr(self.static, op),
|
||||
'jid': {},
|
||||
'node': {}}
|
||||
|
||||
def set_node_handler(self, htype, jid=None, node=None, handler=None):
|
||||
"""
|
||||
Add a node handler for the given hierarchy level and
|
||||
handler type.
|
||||
|
||||
Node handlers are ordered in a hierarchy where the
|
||||
most specific handler is executed. Thus, a fallback,
|
||||
global handler can be used for the majority of cases
|
||||
with a few node specific handler that override the
|
||||
global behavior.
|
||||
|
||||
Node handler hierarchy:
|
||||
JID | Node | Level
|
||||
---------------------
|
||||
None | None | Global
|
||||
Given | None | All nodes for the JID
|
||||
None | Given | Node on self.xmpp.boundjid
|
||||
Given | Given | A single node
|
||||
|
||||
Handler types:
|
||||
get_info
|
||||
get_items
|
||||
set_identities
|
||||
set_features
|
||||
set_items
|
||||
del_items
|
||||
del_identity
|
||||
del_feature
|
||||
del_item
|
||||
add_identity
|
||||
add_feature
|
||||
add_item
|
||||
|
||||
Arguments:
|
||||
htype
|
||||
jid
|
||||
node
|
||||
handler
|
||||
htype -- The operation provided by the handler.
|
||||
jid -- The JID the handler applies to. May be narrowed
|
||||
further if a node is given.
|
||||
node -- The particular node the handler is for. If no JID
|
||||
is given, then the self.xmpp.boundjid.full is
|
||||
assumed.
|
||||
handler -- The handler function to use.
|
||||
"""
|
||||
if htype not in self._disco_ops:
|
||||
return
|
||||
if jid is None and node is None:
|
||||
self.handlers[htype]['global'] = handler
|
||||
self._handlers[htype]['global'] = handler
|
||||
elif node is None:
|
||||
self.handlers[htype]['jid'][jid] = handler
|
||||
self._handlers[htype]['jid'][jid] = handler
|
||||
elif jid is None:
|
||||
jid = self.xmpp.boundjid.full
|
||||
self.handlers[htype]['node'][(jid, node)] = handler
|
||||
self._handlers[htype]['node'][(jid, node)] = handler
|
||||
else:
|
||||
self.handlers[htype]['node'][(jid, node)] = handler
|
||||
self._handlers[htype]['node'][(jid, node)] = handler
|
||||
|
||||
def del_node_handler(self, htype, jid, node):
|
||||
"""
|
||||
Remove a handler type for a JID and node combination.
|
||||
|
||||
The next handler in the hierarchy will be used if one
|
||||
exists. If removing the global handler, make sure that
|
||||
other handlers exist to process existing nodes.
|
||||
|
||||
Node handler hierarchy:
|
||||
JID | Node | Level
|
||||
---------------------
|
||||
None | None | Global
|
||||
Given | None | All nodes for the JID
|
||||
None | Given | Node on self.xmpp.boundjid
|
||||
Given | Given | A single node
|
||||
|
||||
Arguments:
|
||||
htype
|
||||
jid
|
||||
node
|
||||
htype -- The type of handler to remove.
|
||||
jid -- The JID from which to remove the handler.
|
||||
node -- The node from which to remove the handler.
|
||||
"""
|
||||
self.set_node_handler(htype, jid, node, None)
|
||||
|
||||
|
@ -132,14 +215,28 @@ class xep_0030(base_plugin):
|
|||
|
||||
def get_info(self, jid=None, node=None, local=False, **kwargs):
|
||||
"""
|
||||
Retrieve the disco#info results from a given JID/node combination.
|
||||
|
||||
Info may be retrieved from both local resources and remote agents;
|
||||
the local parameter indicates if the information should be gathered
|
||||
by executing the local node handlers, or if a disco#info stanza
|
||||
must be generated and sent.
|
||||
|
||||
Arguments:
|
||||
jid --
|
||||
node --
|
||||
local --
|
||||
dfrom --
|
||||
block --
|
||||
timeout --
|
||||
callback --
|
||||
jid -- Request info from this JID.
|
||||
node -- The particular node to query.
|
||||
local -- If true, then the query is for a JID/node
|
||||
combination handled by this Sleek instance and
|
||||
no stanzas need to be sent.
|
||||
Otherwise, a disco stanza must be sent to the
|
||||
remove JID to retrieve the info.
|
||||
dfrom -- Specifiy the sender's JID.
|
||||
block -- If true, block and wait for the stanzas' reply.
|
||||
timeout -- The time in seconds to block while waiting for
|
||||
a reply. If None, then wait indefinitely.
|
||||
callback -- Optional callback to execute when a reply is
|
||||
received instead of blocking and waiting for
|
||||
the reply.
|
||||
"""
|
||||
if local or jid is None:
|
||||
log.debug("Looking up local disco#info data " + \
|
||||
|
@ -158,14 +255,28 @@ class xep_0030(base_plugin):
|
|||
|
||||
def get_items(self, jid=None, node=None, local=False, **kwargs):
|
||||
"""
|
||||
Retrieve the disco#items results from a given JID/node combination.
|
||||
|
||||
Items may be retrieved from both local resources and remote agents;
|
||||
the local parameter indicates if the items should be gathered by
|
||||
executing the local node handlers, or if a disco#items stanza must
|
||||
be generated and sent.
|
||||
|
||||
Arguments:
|
||||
jid --
|
||||
node --
|
||||
local --
|
||||
dfrom --
|
||||
block --
|
||||
timeout --
|
||||
callback --
|
||||
jid -- Request info from this JID.
|
||||
node -- The particular node to query.
|
||||
local -- If true, then the query is for a JID/node
|
||||
combination handled by this Sleek instance and
|
||||
no stanzas need to be sent.
|
||||
Otherwise, a disco stanza must be sent to the
|
||||
remove JID to retrieve the items.
|
||||
dfrom -- Specifiy the sender's JID.
|
||||
block -- If true, block and wait for the stanzas' reply.
|
||||
timeout -- The time in seconds to block while waiting for
|
||||
a reply. If None, then wait indefinitely.
|
||||
callback -- Optional callback to execute when a reply is
|
||||
received instead of blocking and waiting for
|
||||
the reply.
|
||||
"""
|
||||
if local or jid is None:
|
||||
return self._run_node_handler('get_items', jid, node, kwargs)
|
||||
|
@ -179,37 +290,169 @@ class xep_0030(base_plugin):
|
|||
block=kwargs.get('block', None),
|
||||
callback=kwargs.get('callback', None))
|
||||
|
||||
def set_info(self, jid=None, node=None, **kwargs):
|
||||
self._run_node_handler('set_info', jid, node, kwargs)
|
||||
|
||||
def del_info(self, jid=None, node=None, **kwargs):
|
||||
self._run_node_handler('del_info', jid, node, kwargs)
|
||||
|
||||
def set_items(self, jid=None, node=None, **kwargs):
|
||||
"""
|
||||
Set or replace all items for the specified JID/node combination.
|
||||
|
||||
The given items must be in a list or set where each item is a
|
||||
tuple of the form: (jid, node, name).
|
||||
|
||||
Arguments:
|
||||
jid -- The JID to modify.
|
||||
node -- Optional node to modify.
|
||||
items -- A series of items in tuple format.
|
||||
"""
|
||||
self._run_node_handler('set_items', jid, node, kwargs)
|
||||
|
||||
def del_items(self, jid=None, node=None, **kwargs):
|
||||
"""
|
||||
Remove all items from the given JID/node combination.
|
||||
|
||||
Arguments:
|
||||
jid -- The JID to modify.
|
||||
node -- Optional node to modify.
|
||||
"""
|
||||
self._run_node_handler('del_items', jid, node, kwargs)
|
||||
|
||||
def add_identity(self, jid=None, node=None, **kwargs):
|
||||
self._run_node_handler('add_identity', jid, node, kwargs)
|
||||
|
||||
def add_feature(self, jid=None, node=None, **kwargs):
|
||||
self._run_node_handler('add_feature', jid, node, kwargs)
|
||||
|
||||
def del_identity(self, jid=None, node=None, **kwargs):
|
||||
self._run_node_handler('del_identity', jid, node, kwargs)
|
||||
|
||||
def del_feature(self, jid=None, node=None, **kwargs):
|
||||
self._run_node_handler('del_feature', jid, node, kwargs)
|
||||
|
||||
def add_item(self, jid=None, node=None, **kwargs):
|
||||
"""
|
||||
Add a new item element to the given JID/node combination.
|
||||
|
||||
Each item is required to have a JID, but may also specify
|
||||
a node value to reference non-addressable entities.
|
||||
|
||||
Arguments:
|
||||
jid -- The JID to modify.
|
||||
node -- The node to modify.
|
||||
ijid -- The JID for the item.
|
||||
inode -- Optional node for the item.
|
||||
name -- Optional name for the item.
|
||||
"""
|
||||
self._run_node_handler('add_item', jid, node, kwargs)
|
||||
|
||||
def del_item(self, jid=None, node=None, **kwargs):
|
||||
"""
|
||||
Remove a single item from the given JID/node combination.
|
||||
|
||||
Arguments:
|
||||
jid -- The JID to modify.
|
||||
node -- The node to modify.
|
||||
ijid -- The item's JID.
|
||||
inode -- The item's node.
|
||||
"""
|
||||
self._run_node_handler('del_item', jid, node, kwargs)
|
||||
|
||||
def _run_node_handler(self, htype, jid, node, data=None):
|
||||
def add_identity(self, jid=None, node=None, **kwargs):
|
||||
"""
|
||||
Add a new identity to the given JID/node combination.
|
||||
|
||||
Each identity must be unique in terms of all four identity
|
||||
components: category, type, name, and language.
|
||||
|
||||
Multiple, identical category/type pairs are allowed only
|
||||
if the xml:lang values are different. Likewise, multiple
|
||||
category/type/xml:lang pairs are allowed so long as the
|
||||
names are different. A category and type is always required.
|
||||
|
||||
Arguments:
|
||||
jid -- The JID to modify.
|
||||
node -- The node to modify.
|
||||
category -- The identity's category.
|
||||
itype -- The identity's type.
|
||||
name -- Optional name for the identity.
|
||||
lang -- Optional two-letter language code.
|
||||
"""
|
||||
self._run_node_handler('add_identity', jid, node, kwargs)
|
||||
|
||||
def add_feature(self, jid=None, node=None, **kwargs):
|
||||
"""
|
||||
Add a feature to a JID/node combination.
|
||||
|
||||
Arguments:
|
||||
jid -- The JID to modify.
|
||||
node -- The node to modify.
|
||||
feature -- The namespace of the supported feature.
|
||||
"""
|
||||
self._run_node_handler('add_feature', jid, node, kwargs)
|
||||
|
||||
def del_identity(self, jid=None, node=None, **kwargs):
|
||||
"""
|
||||
Remove an identity from the given JID/node combination.
|
||||
|
||||
Arguments:
|
||||
jid -- The JID to modify.
|
||||
node -- The node to modify.
|
||||
category -- The identity's category.
|
||||
itype -- The identity's type value.
|
||||
name -- Optional, human readable name for the identity.
|
||||
lang -- Optional, the identity's xml:lang value.
|
||||
"""
|
||||
self._run_node_handler('del_identity', jid, node, kwargs)
|
||||
|
||||
def del_feature(self, jid=None, node=None, **kwargs):
|
||||
"""
|
||||
Remove a feature from a given JID/node combination.
|
||||
|
||||
Arguments:
|
||||
jid -- The JID to modify.
|
||||
node -- The node to modify.
|
||||
feature -- The feature's namespace.
|
||||
"""
|
||||
self._run_node_handler('del_feature', jid, node, kwargs)
|
||||
|
||||
def set_identities(self, jid=None, node=None, **kwargs):
|
||||
"""
|
||||
Add or replace all identities for the given JID/node combination.
|
||||
|
||||
The identities must be in a set where each identity is a tuple
|
||||
of the form: (category, type, lang, name)
|
||||
|
||||
Arguments:
|
||||
jid -- The JID to modify.
|
||||
node -- The node to modify.
|
||||
identities -- A set of identities in tuple form.
|
||||
lang -- Optional, xml:lang value.
|
||||
"""
|
||||
self._run_node_handler('set_identities', jid, node, kwargs)
|
||||
|
||||
def del_identities(self, jid=None, node=None, **kwargs):
|
||||
"""
|
||||
Remove all identities for a JID/node combination.
|
||||
|
||||
If a language is specified, only identities using that
|
||||
language will be removed.
|
||||
|
||||
Arguments:
|
||||
jid -- The JID to modify.
|
||||
node -- The node to modify.
|
||||
lang -- Optional. If given, only remove identities
|
||||
using this xml:lang value.
|
||||
"""
|
||||
self._run_node_handler('del_identities', jid, node, kwargs)
|
||||
|
||||
def set_features(self, jid=None, node=None, **kwargs):
|
||||
"""
|
||||
Add or replace the set of supported features
|
||||
for a JID/node combination.
|
||||
|
||||
Arguments:
|
||||
jid -- The JID to modify.
|
||||
node -- The node to modify.
|
||||
features -- The new set of supported features.
|
||||
"""
|
||||
self._run_node_handler('set_features', jid, node, kwargs)
|
||||
|
||||
def del_features(self, jid=None, node=None, **kwargs):
|
||||
"""
|
||||
Remove all features from a JID/node combination.
|
||||
|
||||
Arguments:
|
||||
jid -- The JID to modify.
|
||||
node -- The node to modify.
|
||||
"""
|
||||
self._run_node_handler('del_features', jid, node, kwargs)
|
||||
|
||||
def _run_node_handler(self, htype, jid, node, data={}):
|
||||
"""
|
||||
Execute the most specific node handler for the given
|
||||
JID/node combination.
|
||||
|
@ -218,19 +461,19 @@ class xep_0030(base_plugin):
|
|||
htype -- The handler type to execute.
|
||||
jid -- The JID requested.
|
||||
node -- The node requested.
|
||||
dat -- Optional, custom data to pass to the handler.
|
||||
data -- Optional, custom data to pass to the handler.
|
||||
"""
|
||||
if jid is None:
|
||||
jid = self.xmpp.boundjid.full
|
||||
if node is None:
|
||||
node = ''
|
||||
|
||||
if self.handlers[htype]['node'].get((jid, node), False):
|
||||
return self.handlers[htype]['node'][(jid, node)](jid, node, data)
|
||||
elif self.handlers[htype]['jid'].get(jid, False):
|
||||
return self.handlers[htype]['jid'][jid](jid, node, data)
|
||||
elif self.handlers[htype]['global']:
|
||||
return self.handlers[htype]['global'](jid, node, data)
|
||||
if self._handlers[htype]['node'].get((jid, node), False):
|
||||
return self._handlers[htype]['node'][(jid, node)](jid, node, data)
|
||||
elif self._handlers[htype]['jid'].get(jid, False):
|
||||
return self._handlers[htype]['jid'][jid](jid, node, data)
|
||||
elif self._handlers[htype]['global']:
|
||||
return self._handlers[htype]['global'](jid, node, data)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -311,4 +554,3 @@ class xep_0030(base_plugin):
|
|||
"Using default disco#info feature.")
|
||||
info.add_feature(info.namespace)
|
||||
return info
|
||||
|
||||
|
|
|
@ -12,8 +12,6 @@ from sleekxmpp.xmlstream import ElementBase, ET
|
|||
class DiscoItems(ElementBase):
|
||||
|
||||
"""
|
||||
|
||||
|
||||
Example disco#items stanzas:
|
||||
<iq type="get">
|
||||
<query xmlns="http://jabber.org/protocol/disco#items" />
|
||||
|
@ -74,7 +72,7 @@ class DiscoItems(ElementBase):
|
|||
|
||||
Arguments:
|
||||
jid -- The JID for the item.
|
||||
node -- Optional additional information to reference
|
||||
node -- Optional additional information to reference
|
||||
non-addressable items.
|
||||
name -- Optional human readable name for the item.
|
||||
"""
|
||||
|
|
|
@ -35,6 +35,11 @@ class StaticDisco(object):
|
|||
|
||||
def __init__(self, xmpp):
|
||||
"""
|
||||
Create a static disco interface. Sets of disco#info and
|
||||
disco#items are maintained for every given JID and node
|
||||
combination. These stanzas are used to store disco
|
||||
information in memory without any additional processing.
|
||||
|
||||
Arguments:
|
||||
xmpp -- The main SleekXMPP object.
|
||||
"""
|
||||
|
@ -52,7 +57,7 @@ class StaticDisco(object):
|
|||
self.nodes[(jid, node)]['info']['node'] = node
|
||||
self.nodes[(jid, node)]['items']['node'] = node
|
||||
|
||||
def get_info(self, jid, node, data=None):
|
||||
def get_info(self, jid, node, data):
|
||||
if (jid, node) not in self.nodes:
|
||||
if not node:
|
||||
return DiscoInfo()
|
||||
|
@ -61,11 +66,11 @@ class StaticDisco(object):
|
|||
else:
|
||||
return self.nodes[(jid, node)]['info']
|
||||
|
||||
def del_info(self, jid, node, data=None):
|
||||
def del_info(self, jid, node, data):
|
||||
if (jid, node) in self.nodes:
|
||||
self.nodes[(jid, node)]['info'] = DiscoInfo()
|
||||
|
||||
def get_items(self, jid, node, data=None):
|
||||
def get_items(self, jid, node, data):
|
||||
if (jid, node) not in self.nodes:
|
||||
if not node:
|
||||
return DiscoInfo()
|
||||
|
@ -74,14 +79,16 @@ class StaticDisco(object):
|
|||
else:
|
||||
return self.nodes[(jid, node)]['items']
|
||||
|
||||
def set_items(self, jid, node, data=None):
|
||||
pass
|
||||
def set_items(self, jid, node, data):
|
||||
items = data.get('items', set())
|
||||
self.add_node(jid, node)
|
||||
self.nodes[(jid, node)]['items']['items'] = items
|
||||
|
||||
def del_items(self, jid, node, data=None):
|
||||
def del_items(self, jid, node, data):
|
||||
if (jid, node) in self.nodes:
|
||||
self.nodes[(jid, node)]['items'] = DiscoItems()
|
||||
|
||||
def add_identity(self, jid, node, data={}):
|
||||
def add_identity(self, jid, node, data):
|
||||
self.add_node(jid, node)
|
||||
self.nodes[(jid, node)]['info'].add_identity(
|
||||
data.get('category', ''),
|
||||
|
@ -89,10 +96,12 @@ class StaticDisco(object):
|
|||
data.get('name', None),
|
||||
data.get('lang', None))
|
||||
|
||||
def set_identities(self, jid, node, data=None):
|
||||
pass
|
||||
def set_identities(self, jid, node, data):
|
||||
identities = data.get('identities', set())
|
||||
self.add_node(jid, node)
|
||||
self.nodes[(jid, node)]['info']['identities'] = identities
|
||||
|
||||
def del_identity(self, jid, node, data=None):
|
||||
def del_identity(self, jid, node, data):
|
||||
if (jid, node) not in self.nodes:
|
||||
return
|
||||
self.nodes[(jid, node)]['info'].del_identity(
|
||||
|
@ -101,27 +110,29 @@ class StaticDisco(object):
|
|||
data.get('name', None),
|
||||
data.get('lang', None))
|
||||
|
||||
|
||||
def add_feature(self, jid, node, data=None):
|
||||
def add_feature(self, jid, node, data):
|
||||
self.add_node(jid, node)
|
||||
self.nodes[(jid, node)]['info'].add_feature(data.get('feature', ''))
|
||||
|
||||
def set_features(self, jid, node, data=None):
|
||||
pass
|
||||
def set_features(self, jid, node, data):
|
||||
features = data.get('features', set())
|
||||
self.add_node(jid, node)
|
||||
self.nodes[(jid, node)]['info']['features'] = features
|
||||
|
||||
def del_feature(self, jid, node, data=None):
|
||||
def del_feature(self, jid, node, data):
|
||||
if (jid, node) not in self.nodes:
|
||||
return
|
||||
self.nodes[(jid, node)]['info'].del_feature(data.get('feature', ''))
|
||||
|
||||
def add_item(self, jid, node, data=None):
|
||||
def add_item(self, jid, node, data):
|
||||
self.add_node(jid, node)
|
||||
self.nodes[(jid, node)]['items'].add_item(
|
||||
data.get('ijid', ''),
|
||||
node=data.get('inode', None),
|
||||
name=data.get('name', None))
|
||||
|
||||
def del_item(self, jid, node, data=None):
|
||||
def del_item(self, jid, node, data):
|
||||
if (jid, node) in self.nodes:
|
||||
self.nodes[(jid, node)]['items'].del_item(**data)
|
||||
|
||||
self.nodes[(jid, node)]['items'].del_item(
|
||||
data.get('ijid', ''),
|
||||
node=data.get('inode', None))
|
||||
|
|
|
@ -20,325 +20,333 @@ log = logging.getLogger(__name__)
|
|||
|
||||
|
||||
class MUCPresence(ElementBase):
|
||||
name = 'x'
|
||||
namespace = 'http://jabber.org/protocol/muc#user'
|
||||
plugin_attrib = 'muc'
|
||||
interfaces = set(('affiliation', 'role', 'jid', 'nick', 'room'))
|
||||
affiliations = set(('', ))
|
||||
roles = set(('', ))
|
||||
name = 'x'
|
||||
namespace = 'http://jabber.org/protocol/muc#user'
|
||||
plugin_attrib = 'muc'
|
||||
interfaces = set(('affiliation', 'role', 'jid', 'nick', 'room'))
|
||||
affiliations = set(('', ))
|
||||
roles = set(('', ))
|
||||
|
||||
def getXMLItem(self):
|
||||
item = self.xml.find('{http://jabber.org/protocol/muc#user}item')
|
||||
if item is None:
|
||||
item = ET.Element('{http://jabber.org/protocol/muc#user}item')
|
||||
self.xml.append(item)
|
||||
return item
|
||||
def getXMLItem(self):
|
||||
item = self.xml.find('{http://jabber.org/protocol/muc#user}item')
|
||||
if item is None:
|
||||
item = ET.Element('{http://jabber.org/protocol/muc#user}item')
|
||||
self.xml.append(item)
|
||||
return item
|
||||
|
||||
def getAffiliation(self):
|
||||
#TODO if no affilation, set it to the default and return default
|
||||
item = self.getXMLItem()
|
||||
return item.get('affiliation', '')
|
||||
def getAffiliation(self):
|
||||
#TODO if no affilation, set it to the default and return default
|
||||
item = self.getXMLItem()
|
||||
return item.get('affiliation', '')
|
||||
|
||||
def setAffiliation(self, value):
|
||||
item = self.getXMLItem()
|
||||
#TODO check for valid affiliation
|
||||
item.attrib['affiliation'] = value
|
||||
return self
|
||||
def setAffiliation(self, value):
|
||||
item = self.getXMLItem()
|
||||
#TODO check for valid affiliation
|
||||
item.attrib['affiliation'] = value
|
||||
return self
|
||||
|
||||
def delAffiliation(self):
|
||||
item = self.getXMLItem()
|
||||
#TODO set default affiliation
|
||||
if 'affiliation' in item.attrib: del item.attrib['affiliation']
|
||||
return self
|
||||
def delAffiliation(self):
|
||||
item = self.getXMLItem()
|
||||
#TODO set default affiliation
|
||||
if 'affiliation' in item.attrib: del item.attrib['affiliation']
|
||||
return self
|
||||
|
||||
def getJid(self):
|
||||
item = self.getXMLItem()
|
||||
return JID(item.get('jid', ''))
|
||||
def getJid(self):
|
||||
item = self.getXMLItem()
|
||||
return JID(item.get('jid', ''))
|
||||
|
||||
def setJid(self, value):
|
||||
item = self.getXMLItem()
|
||||
if not isinstance(value, str):
|
||||
value = str(value)
|
||||
item.attrib['jid'] = value
|
||||
return self
|
||||
def setJid(self, value):
|
||||
item = self.getXMLItem()
|
||||
if not isinstance(value, str):
|
||||
value = str(value)
|
||||
item.attrib['jid'] = value
|
||||
return self
|
||||
|
||||
def delJid(self):
|
||||
item = self.getXMLItem()
|
||||
if 'jid' in item.attrib: del item.attrib['jid']
|
||||
return self
|
||||
def delJid(self):
|
||||
item = self.getXMLItem()
|
||||
if 'jid' in item.attrib: del item.attrib['jid']
|
||||
return self
|
||||
|
||||
def getRole(self):
|
||||
item = self.getXMLItem()
|
||||
#TODO get default role, set default role if none
|
||||
return item.get('role', '')
|
||||
def getRole(self):
|
||||
item = self.getXMLItem()
|
||||
#TODO get default role, set default role if none
|
||||
return item.get('role', '')
|
||||
|
||||
def setRole(self, value):
|
||||
item = self.getXMLItem()
|
||||
#TODO check for valid role
|
||||
item.attrib['role'] = value
|
||||
return self
|
||||
def setRole(self, value):
|
||||
item = self.getXMLItem()
|
||||
#TODO check for valid role
|
||||
item.attrib['role'] = value
|
||||
return self
|
||||
|
||||
def delRole(self):
|
||||
item = self.getXMLItem()
|
||||
#TODO set default role
|
||||
if 'role' in item.attrib: del item.attrib['role']
|
||||
return self
|
||||
def delRole(self):
|
||||
item = self.getXMLItem()
|
||||
#TODO set default role
|
||||
if 'role' in item.attrib: del item.attrib['role']
|
||||
return self
|
||||
|
||||
def getNick(self):
|
||||
return self.parent()['from'].resource
|
||||
def getNick(self):
|
||||
return self.parent()['from'].resource
|
||||
|
||||
def getRoom(self):
|
||||
return self.parent()['from'].bare
|
||||
def getRoom(self):
|
||||
return self.parent()['from'].bare
|
||||
|
||||
def setNick(self, value):
|
||||
log.warning("Cannot set nick through mucpresence plugin.")
|
||||
return self
|
||||
def setNick(self, value):
|
||||
log.warning("Cannot set nick through mucpresence plugin.")
|
||||
return self
|
||||
|
||||
def setRoom(self, value):
|
||||
log.warning("Cannot set room through mucpresence plugin.")
|
||||
return self
|
||||
def setRoom(self, value):
|
||||
log.warning("Cannot set room through mucpresence plugin.")
|
||||
return self
|
||||
|
||||
def delNick(self):
|
||||
log.warning("Cannot delete nick through mucpresence plugin.")
|
||||
return self
|
||||
def delNick(self):
|
||||
log.warning("Cannot delete nick through mucpresence plugin.")
|
||||
return self
|
||||
|
||||
def delRoom(self):
|
||||
log.warning("Cannot delete room through mucpresence plugin.")
|
||||
return self
|
||||
def delRoom(self):
|
||||
log.warning("Cannot delete room through mucpresence plugin.")
|
||||
return self
|
||||
|
||||
class xep_0045(base.base_plugin):
|
||||
"""
|
||||
Impliments XEP-0045 Multi User Chat
|
||||
"""
|
||||
"""
|
||||
Implements XEP-0045 Multi User Chat
|
||||
"""
|
||||
|
||||
def plugin_init(self):
|
||||
self.rooms = {}
|
||||
self.ourNicks = {}
|
||||
self.xep = '0045'
|
||||
self.description = 'Multi User Chat'
|
||||
# load MUC support in presence stanzas
|
||||
registerStanzaPlugin(Presence, MUCPresence)
|
||||
self.xmpp.registerHandler(Callback('MUCPresence', MatchXMLMask("<presence xmlns='%s' />" % self.xmpp.default_ns), self.handle_groupchat_presence))
|
||||
self.xmpp.registerHandler(Callback('MUCMessage', MatchXMLMask("<message xmlns='%s' type='groupchat'><body/></message>" % self.xmpp.default_ns), self.handle_groupchat_message))
|
||||
self.xmpp.registerHandler(Callback('MUCSubject', MatchXMLMask("<message xmlns='%s' type='groupchat'><subject/></message>" % self.xmpp.default_ns), self.handle_groupchat_subject))
|
||||
def plugin_init(self):
|
||||
self.rooms = {}
|
||||
self.ourNicks = {}
|
||||
self.xep = '0045'
|
||||
self.description = 'Multi User Chat'
|
||||
# load MUC support in presence stanzas
|
||||
registerStanzaPlugin(Presence, MUCPresence)
|
||||
self.xmpp.registerHandler(Callback('MUCPresence', MatchXMLMask("<presence xmlns='%s' />" % self.xmpp.default_ns), self.handle_groupchat_presence))
|
||||
self.xmpp.registerHandler(Callback('MUCMessage', MatchXMLMask("<message xmlns='%s' type='groupchat'><body/></message>" % self.xmpp.default_ns), self.handle_groupchat_message))
|
||||
self.xmpp.registerHandler(Callback('MUCSubject', MatchXMLMask("<message xmlns='%s' type='groupchat'><subject/></message>" % self.xmpp.default_ns), self.handle_groupchat_subject))
|
||||
self.xmpp.registerHandler(Callback('MUCInvite', MatchXPath("{%s}message/{http://jabber.org/protocol/muc#user}x/invite" % self.xmpp.default_ns), self.handle_groupchat_invite))
|
||||
|
||||
def handle_groupchat_presence(self, pr):
|
||||
""" Handle a presence in a muc.
|
||||
"""
|
||||
got_offline = False
|
||||
got_online = False
|
||||
if pr['muc']['room'] not in self.rooms.keys():
|
||||
return
|
||||
entry = pr['muc'].getStanzaValues()
|
||||
entry['show'] = pr['show']
|
||||
entry['status'] = pr['status']
|
||||
if pr['type'] == 'unavailable':
|
||||
if entry['nick'] in self.rooms[entry['room']]:
|
||||
del self.rooms[entry['room']][entry['nick']]
|
||||
got_offline = True
|
||||
else:
|
||||
if entry['nick'] not in self.rooms[entry['room']]:
|
||||
got_online = True
|
||||
self.rooms[entry['room']][entry['nick']] = entry
|
||||
log.debug("MUC presence from %s/%s : %s" % (entry['room'],entry['nick'], entry))
|
||||
self.xmpp.event("groupchat_presence", pr)
|
||||
self.xmpp.event("muc::%s::presence" % entry['room'], pr)
|
||||
if got_offline:
|
||||
self.xmpp.event("muc::%s::got_offline" % entry['room'], pr)
|
||||
if got_online:
|
||||
self.xmpp.event("muc::%s::got_online" % entry['room'], pr)
|
||||
def handle_groupchat_invite(self, inv):
|
||||
""" Handle an invite into a muc.
|
||||
"""
|
||||
logging.debug("MUC invite to %s from %s: %s" % (inv['from'], inv["from"], inv))
|
||||
if inv['from'] not in self.rooms.keys():
|
||||
self.xmpp.event("groupchat_invite", inv)
|
||||
|
||||
def handle_groupchat_message(self, msg):
|
||||
""" Handle a message event in a muc.
|
||||
"""
|
||||
self.xmpp.event('groupchat_message', msg)
|
||||
self.xmpp.event("muc::%s::message" % msg['from'].bare, msg)
|
||||
def handle_groupchat_presence(self, pr):
|
||||
""" Handle a presence in a muc.
|
||||
"""
|
||||
got_offline = False
|
||||
got_online = False
|
||||
if pr['muc']['room'] not in self.rooms.keys():
|
||||
return
|
||||
entry = pr['muc'].getStanzaValues()
|
||||
entry['show'] = pr['show']
|
||||
entry['status'] = pr['status']
|
||||
if pr['type'] == 'unavailable':
|
||||
if entry['nick'] in self.rooms[entry['room']]:
|
||||
del self.rooms[entry['room']][entry['nick']]
|
||||
got_offline = True
|
||||
else:
|
||||
if entry['nick'] not in self.rooms[entry['room']]:
|
||||
got_online = True
|
||||
self.rooms[entry['room']][entry['nick']] = entry
|
||||
log.debug("MUC presence from %s/%s : %s" % (entry['room'],entry['nick'], entry))
|
||||
self.xmpp.event("groupchat_presence", pr)
|
||||
self.xmpp.event("muc::%s::presence" % entry['room'], pr)
|
||||
if got_offline:
|
||||
self.xmpp.event("muc::%s::got_offline" % entry['room'], pr)
|
||||
if got_online:
|
||||
self.xmpp.event("muc::%s::got_online" % entry['room'], pr)
|
||||
|
||||
def handle_groupchat_subject(self, msg):
|
||||
""" Handle a message coming from a muc indicating
|
||||
a change of subject (or announcing it when joining the room)
|
||||
"""
|
||||
self.xmpp.event('groupchat_subject', msg)
|
||||
def handle_groupchat_message(self, msg):
|
||||
""" Handle a message event in a muc.
|
||||
"""
|
||||
self.xmpp.event('groupchat_message', msg)
|
||||
self.xmpp.event("muc::%s::message" % msg['from'].bare, msg)
|
||||
|
||||
def jidInRoom(self, room, jid):
|
||||
for nick in self.rooms[room]:
|
||||
entry = self.rooms[room][nick]
|
||||
if entry is not None and entry['jid'].full == jid:
|
||||
return True
|
||||
return False
|
||||
def handle_groupchat_subject(self, msg):
|
||||
""" Handle a message coming from a muc indicating
|
||||
a change of subject (or announcing it when joining the room)
|
||||
"""
|
||||
self.xmpp.event('groupchat_subject', msg)
|
||||
|
||||
def getNick(self, room, jid):
|
||||
for nick in self.rooms[room]:
|
||||
entry = self.rooms[room][nick]
|
||||
if entry is not None and entry['jid'].full == jid:
|
||||
return nick
|
||||
def jidInRoom(self, room, jid):
|
||||
for nick in self.rooms[room]:
|
||||
entry = self.rooms[room][nick]
|
||||
if entry is not None and entry['jid'].full == jid:
|
||||
return True
|
||||
return False
|
||||
|
||||
def getRoomForm(self, room, ifrom=None):
|
||||
iq = self.xmpp.makeIqGet()
|
||||
iq['to'] = room
|
||||
if ifrom is not None:
|
||||
iq['from'] = ifrom
|
||||
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
|
||||
iq.append(query)
|
||||
result = iq.send()
|
||||
if result['type'] == 'error':
|
||||
return False
|
||||
xform = result.xml.find('{http://jabber.org/protocol/muc#owner}query/{jabber:x:data}x')
|
||||
if xform is None: return False
|
||||
form = self.xmpp.plugin['old_0004'].buildForm(xform)
|
||||
return form
|
||||
def getNick(self, room, jid):
|
||||
for nick in self.rooms[room]:
|
||||
entry = self.rooms[room][nick]
|
||||
if entry is not None and entry['jid'].full == jid:
|
||||
return nick
|
||||
|
||||
def configureRoom(self, room, form=None, ifrom=None):
|
||||
if form is None:
|
||||
form = self.getRoomForm(room, ifrom=ifrom)
|
||||
#form = self.xmpp.plugin['old_0004'].makeForm(ftype='submit')
|
||||
#form.addField('FORM_TYPE', value='http://jabber.org/protocol/muc#roomconfig')
|
||||
iq = self.xmpp.makeIqSet()
|
||||
iq['to'] = room
|
||||
if ifrom is not None:
|
||||
iq['from'] = ifrom
|
||||
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
|
||||
form = form.getXML('submit')
|
||||
query.append(form)
|
||||
iq.append(query)
|
||||
result = iq.send()
|
||||
if result['type'] == 'error':
|
||||
return False
|
||||
return True
|
||||
def getRoomForm(self, room, ifrom=None):
|
||||
iq = self.xmpp.makeIqGet()
|
||||
iq['to'] = room
|
||||
if ifrom is not None:
|
||||
iq['from'] = ifrom
|
||||
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
|
||||
iq.append(query)
|
||||
result = iq.send()
|
||||
if result['type'] == 'error':
|
||||
return False
|
||||
xform = result.xml.find('{http://jabber.org/protocol/muc#owner}query/{jabber:x:data}x')
|
||||
if xform is None: return False
|
||||
form = self.xmpp.plugin['old_0004'].buildForm(xform)
|
||||
return form
|
||||
|
||||
def joinMUC(self, room, nick, maxhistory="0", password='', wait=False, pstatus=None, pshow=None):
|
||||
""" Join the specified room, requesting 'maxhistory' lines of history.
|
||||
"""
|
||||
stanza = self.xmpp.makePresence(pto="%s/%s" % (room, nick), pstatus=pstatus, pshow=pshow)
|
||||
x = ET.Element('{http://jabber.org/protocol/muc}x')
|
||||
if password:
|
||||
passelement = ET.Element('password')
|
||||
passelement.text = password
|
||||
x.append(passelement)
|
||||
if maxhistory:
|
||||
history = ET.Element('history')
|
||||
if maxhistory == "0":
|
||||
history.attrib['maxchars'] = maxhistory
|
||||
else:
|
||||
history.attrib['maxstanzas'] = maxhistory
|
||||
x.append(history)
|
||||
stanza.append(x)
|
||||
if not wait:
|
||||
self.xmpp.send(stanza)
|
||||
else:
|
||||
#wait for our own room presence back
|
||||
expect = ET.Element("{%s}presence" % self.xmpp.default_ns, {'from':"%s/%s" % (room, nick)})
|
||||
self.xmpp.send(stanza, expect)
|
||||
self.rooms[room] = {}
|
||||
self.ourNicks[room] = nick
|
||||
def configureRoom(self, room, form=None, ifrom=None):
|
||||
if form is None:
|
||||
form = self.getRoomForm(room, ifrom=ifrom)
|
||||
#form = self.xmpp.plugin['old_0004'].makeForm(ftype='submit')
|
||||
#form.addField('FORM_TYPE', value='http://jabber.org/protocol/muc#roomconfig')
|
||||
iq = self.xmpp.makeIqSet()
|
||||
iq['to'] = room
|
||||
if ifrom is not None:
|
||||
iq['from'] = ifrom
|
||||
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
|
||||
form = form.getXML('submit')
|
||||
query.append(form)
|
||||
iq.append(query)
|
||||
result = iq.send()
|
||||
if result['type'] == 'error':
|
||||
return False
|
||||
return True
|
||||
|
||||
def destroy(self, room, reason='', altroom = '', ifrom=None):
|
||||
iq = self.xmpp.makeIqSet()
|
||||
if ifrom is not None:
|
||||
iq['from'] = ifrom
|
||||
iq['to'] = room
|
||||
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
|
||||
destroy = ET.Element('destroy')
|
||||
if altroom:
|
||||
destroy.attrib['jid'] = altroom
|
||||
xreason = ET.Element('reason')
|
||||
xreason.text = reason
|
||||
destroy.append(xreason)
|
||||
query.append(destroy)
|
||||
iq.append(query)
|
||||
r = iq.send()
|
||||
if r is False or r['type'] == 'error':
|
||||
return False
|
||||
return True
|
||||
def joinMUC(self, room, nick, maxhistory="0", password='', wait=False, pstatus=None, pshow=None):
|
||||
""" Join the specified room, requesting 'maxhistory' lines of history.
|
||||
"""
|
||||
stanza = self.xmpp.makePresence(pto="%s/%s" % (room, nick), pstatus=pstatus, pshow=pshow)
|
||||
x = ET.Element('{http://jabber.org/protocol/muc}x')
|
||||
if password:
|
||||
passelement = ET.Element('password')
|
||||
passelement.text = password
|
||||
x.append(passelement)
|
||||
if maxhistory:
|
||||
history = ET.Element('history')
|
||||
if maxhistory == "0":
|
||||
history.attrib['maxchars'] = maxhistory
|
||||
else:
|
||||
history.attrib['maxstanzas'] = maxhistory
|
||||
x.append(history)
|
||||
stanza.append(x)
|
||||
if not wait:
|
||||
self.xmpp.send(stanza)
|
||||
else:
|
||||
#wait for our own room presence back
|
||||
expect = ET.Element("{%s}presence" % self.xmpp.default_ns, {'from':"%s/%s" % (room, nick)})
|
||||
self.xmpp.send(stanza, expect)
|
||||
self.rooms[room] = {}
|
||||
self.ourNicks[room] = nick
|
||||
|
||||
def setAffiliation(self, room, jid=None, nick=None, affiliation='member'):
|
||||
""" Change room affiliation."""
|
||||
if affiliation not in ('outcast', 'member', 'admin', 'owner', 'none'):
|
||||
raise TypeError
|
||||
query = ET.Element('{http://jabber.org/protocol/muc#admin}query')
|
||||
if nick is not None:
|
||||
item = ET.Element('item', {'affiliation':affiliation, 'nick':nick})
|
||||
else:
|
||||
item = ET.Element('item', {'affiliation':affiliation, 'jid':jid})
|
||||
query.append(item)
|
||||
iq = self.xmpp.makeIqSet(query)
|
||||
iq['to'] = room
|
||||
result = iq.send()
|
||||
if result is False or result['type'] != 'result':
|
||||
raise ValueError
|
||||
return True
|
||||
def destroy(self, room, reason='', altroom = '', ifrom=None):
|
||||
iq = self.xmpp.makeIqSet()
|
||||
if ifrom is not None:
|
||||
iq['from'] = ifrom
|
||||
iq['to'] = room
|
||||
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
|
||||
destroy = ET.Element('destroy')
|
||||
if altroom:
|
||||
destroy.attrib['jid'] = altroom
|
||||
xreason = ET.Element('reason')
|
||||
xreason.text = reason
|
||||
destroy.append(xreason)
|
||||
query.append(destroy)
|
||||
iq.append(query)
|
||||
r = iq.send()
|
||||
if r is False or r['type'] == 'error':
|
||||
return False
|
||||
return True
|
||||
|
||||
def invite(self, room, jid, reason=''):
|
||||
""" Invite a jid to a room."""
|
||||
msg = self.xmpp.makeMessage(room)
|
||||
msg['from'] = self.xmpp.jid
|
||||
x = ET.Element('{http://jabber.org/protocol/muc#user}x')
|
||||
invite = ET.Element('{http://jabber.org/protocol/muc#user}invite', {'to': jid})
|
||||
if reason:
|
||||
rxml = ET.Element('reason')
|
||||
rxml.text = reason
|
||||
invite.append(rxml)
|
||||
x.append(invite)
|
||||
msg.append(x)
|
||||
self.xmpp.send(msg)
|
||||
def setAffiliation(self, room, jid=None, nick=None, affiliation='member'):
|
||||
""" Change room affiliation."""
|
||||
if affiliation not in ('outcast', 'member', 'admin', 'owner', 'none'):
|
||||
raise TypeError
|
||||
query = ET.Element('{http://jabber.org/protocol/muc#admin}query')
|
||||
if nick is not None:
|
||||
item = ET.Element('item', {'affiliation':affiliation, 'nick':nick})
|
||||
else:
|
||||
item = ET.Element('item', {'affiliation':affiliation, 'jid':jid})
|
||||
query.append(item)
|
||||
iq = self.xmpp.makeIqSet(query)
|
||||
iq['to'] = room
|
||||
result = iq.send()
|
||||
if result is False or result['type'] != 'result':
|
||||
raise ValueError
|
||||
return True
|
||||
|
||||
def leaveMUC(self, room, nick, msg=''):
|
||||
""" Leave the specified room.
|
||||
"""
|
||||
if msg:
|
||||
self.xmpp.sendPresence(pshow='unavailable', pto="%s/%s" % (room, nick), pstatus=msg)
|
||||
else:
|
||||
self.xmpp.sendPresence(pshow='unavailable', pto="%s/%s" % (room, nick))
|
||||
del self.rooms[room]
|
||||
def invite(self, room, jid, reason='', mfrom=''):
|
||||
""" Invite a jid to a room."""
|
||||
msg = self.xmpp.makeMessage(room)
|
||||
msg['from'] = mfrom
|
||||
x = ET.Element('{http://jabber.org/protocol/muc#user}x')
|
||||
invite = ET.Element('{http://jabber.org/protocol/muc#user}invite', {'to': jid})
|
||||
if reason:
|
||||
rxml = ET.Element('reason')
|
||||
rxml.text = reason
|
||||
invite.append(rxml)
|
||||
x.append(invite)
|
||||
msg.append(x)
|
||||
self.xmpp.send(msg)
|
||||
|
||||
def getRoomConfig(self, room):
|
||||
iq = self.xmpp.makeIqGet('http://jabber.org/protocol/muc#owner')
|
||||
iq['to'] = room
|
||||
iq['from'] = self.xmpp.jid
|
||||
result = iq.send()
|
||||
if result is None or result['type'] != 'result':
|
||||
raise ValueError
|
||||
form = result.xml.find('{http://jabber.org/protocol/muc#owner}query/{jabber:x:data}x')
|
||||
if form is None:
|
||||
raise ValueError
|
||||
return self.xmpp.plugin['xep_0004'].buildForm(form)
|
||||
def leaveMUC(self, room, nick, msg=''):
|
||||
""" Leave the specified room.
|
||||
"""
|
||||
if msg:
|
||||
self.xmpp.sendPresence(pshow='unavailable', pto="%s/%s" % (room, nick), pstatus=msg)
|
||||
else:
|
||||
self.xmpp.sendPresence(pshow='unavailable', pto="%s/%s" % (room, nick))
|
||||
del self.rooms[room]
|
||||
|
||||
def cancelConfig(self, room):
|
||||
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
|
||||
x = ET.Element('{jabber:x:data}x', type='cancel')
|
||||
query.append(x)
|
||||
iq = self.xmpp.makeIqSet(query)
|
||||
iq.send()
|
||||
def getRoomConfig(self, room, ifrom=''):
|
||||
iq = self.xmpp.makeIqGet('http://jabber.org/protocol/muc#owner')
|
||||
iq['to'] = room
|
||||
iq['from'] = ifrom
|
||||
result = iq.send()
|
||||
if result is None or result['type'] != 'result':
|
||||
raise ValueError
|
||||
form = result.xml.find('{http://jabber.org/protocol/muc#owner}query/{jabber:x:data}x')
|
||||
if form is None:
|
||||
raise ValueError
|
||||
return self.xmpp.plugin['xep_0004'].buildForm(form)
|
||||
|
||||
def setRoomConfig(self, room, config):
|
||||
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
|
||||
x = config.getXML('submit')
|
||||
query.append(x)
|
||||
iq = self.xmpp.makeIqSet(query)
|
||||
iq['to'] = room
|
||||
iq['from'] = self.xmpp.jid
|
||||
iq.send()
|
||||
def cancelConfig(self, room):
|
||||
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
|
||||
x = ET.Element('{jabber:x:data}x', type='cancel')
|
||||
query.append(x)
|
||||
iq = self.xmpp.makeIqSet(query)
|
||||
iq.send()
|
||||
|
||||
def getJoinedRooms(self):
|
||||
return self.rooms.keys()
|
||||
def setRoomConfig(self, room, config, ifrom=''):
|
||||
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
|
||||
x = config.getXML('submit')
|
||||
query.append(x)
|
||||
iq = self.xmpp.makeIqSet(query)
|
||||
iq['to'] = room
|
||||
iq['from'] = ifrom
|
||||
iq.send()
|
||||
|
||||
def getOurJidInRoom(self, roomJid):
|
||||
""" Return the jid we're using in a room.
|
||||
"""
|
||||
return "%s/%s" % (roomJid, self.ourNicks[roomJid])
|
||||
def getJoinedRooms(self):
|
||||
return self.rooms.keys()
|
||||
|
||||
def getJidProperty(self, room, nick, jidProperty):
|
||||
""" Get the property of a nick in a room, such as its 'jid' or 'affiliation'
|
||||
If not found, return None.
|
||||
"""
|
||||
if room in self.rooms and nick in self.rooms[room] and jidProperty in self.rooms[room][nick]:
|
||||
return self.rooms[room][nick][jidProperty]
|
||||
else:
|
||||
return None
|
||||
def getOurJidInRoom(self, roomJid):
|
||||
""" Return the jid we're using in a room.
|
||||
"""
|
||||
return "%s/%s" % (roomJid, self.ourNicks[roomJid])
|
||||
|
||||
def getRoster(self, room):
|
||||
""" Get the list of nicks in a room.
|
||||
"""
|
||||
if room not in self.rooms.keys():
|
||||
return None
|
||||
return self.rooms[room].keys()
|
||||
def getJidProperty(self, room, nick, jidProperty):
|
||||
""" Get the property of a nick in a room, such as its 'jid' or 'affiliation'
|
||||
If not found, return None.
|
||||
"""
|
||||
if room in self.rooms and nick in self.rooms[room] and jidProperty in self.rooms[room][nick]:
|
||||
return self.rooms[room][nick][jidProperty]
|
||||
else:
|
||||
return None
|
||||
|
||||
def getRoster(self, room):
|
||||
""" Get the list of nicks in a room.
|
||||
"""
|
||||
if room not in self.rooms.keys():
|
||||
return None
|
||||
return self.rooms[room].keys()
|
||||
|
|
|
@ -110,7 +110,7 @@ class xep_0050(base.base_plugin):
|
|||
if not id:
|
||||
id = self.xmpp.getNewId()
|
||||
iq = self.xmpp.makeIqResult(id)
|
||||
iq.attrib['from'] = self.xmpp.fulljid
|
||||
iq.attrib['from'] = self.xmpp.boundjid.full
|
||||
iq.attrib['to'] = to
|
||||
command = ET.Element('{http://jabber.org/protocol/commands}command')
|
||||
command.attrib['node'] = node
|
||||
|
|
|
@ -51,7 +51,7 @@ class xep_0060(base.base_plugin):
|
|||
pubsub.append(configure)
|
||||
iq = self.xmpp.makeIqSet(pubsub)
|
||||
iq.attrib['to'] = jid
|
||||
iq.attrib['from'] = self.xmpp.fulljid
|
||||
iq.attrib['from'] = self.xmpp.boundjid.full
|
||||
id = iq['id']
|
||||
result = iq.send()
|
||||
if result is False or result is None or result['type'] == 'error': return False
|
||||
|
@ -63,15 +63,15 @@ class xep_0060(base.base_plugin):
|
|||
subscribe.attrib['node'] = node
|
||||
if subscribee is None:
|
||||
if bare:
|
||||
subscribe.attrib['jid'] = self.xmpp.jid
|
||||
subscribe.attrib['jid'] = self.xmpp.boundjid.bare
|
||||
else:
|
||||
subscribe.attrib['jid'] = self.xmpp.fulljid
|
||||
subscribe.attrib['jid'] = self.xmpp.boundjid.full
|
||||
else:
|
||||
subscribe.attrib['jid'] = subscribee
|
||||
pubsub.append(subscribe)
|
||||
iq = self.xmpp.makeIqSet(pubsub)
|
||||
iq.attrib['to'] = jid
|
||||
iq.attrib['from'] = self.xmpp.fulljid
|
||||
iq.attrib['from'] = self.xmpp.boundjid.full
|
||||
id = iq['id']
|
||||
result = iq.send()
|
||||
if result is False or result is None or result['type'] == 'error': return False
|
||||
|
@ -83,15 +83,15 @@ class xep_0060(base.base_plugin):
|
|||
unsubscribe.attrib['node'] = node
|
||||
if subscribee is None:
|
||||
if bare:
|
||||
unsubscribe.attrib['jid'] = self.xmpp.jid
|
||||
unsubscribe.attrib['jid'] = self.xmpp.boundjid.bare
|
||||
else:
|
||||
unsubscribe.attrib['jid'] = self.xmpp.fulljid
|
||||
unsubscribe.attrib['jid'] = self.xmpp.boundjid.full
|
||||
else:
|
||||
unsubscribe.attrib['jid'] = subscribee
|
||||
pubsub.append(unsubscribe)
|
||||
iq = self.xmpp.makeIqSet(pubsub)
|
||||
iq.attrib['to'] = jid
|
||||
iq.attrib['from'] = self.xmpp.fulljid
|
||||
iq.attrib['from'] = self.xmpp.boundjid.full
|
||||
id = iq['id']
|
||||
result = iq.send()
|
||||
if result is False or result is None or result['type'] == 'error': return False
|
||||
|
@ -109,7 +109,7 @@ class xep_0060(base.base_plugin):
|
|||
iq = self.xmpp.makeIqGet()
|
||||
iq.append(pubsub)
|
||||
iq.attrib['to'] = jid
|
||||
iq.attrib['from'] = self.xmpp.fulljid
|
||||
iq.attrib['from'] = self.xmpp.boundjid.full
|
||||
id = iq['id']
|
||||
#self.xmpp.add_handler("<iq id='%s'/>" % id, self.handlerCreateNodeResponse)
|
||||
result = iq.send()
|
||||
|
@ -133,7 +133,7 @@ class xep_0060(base.base_plugin):
|
|||
iq = self.xmpp.makeIqGet()
|
||||
iq.append(pubsub)
|
||||
iq.attrib['to'] = jid
|
||||
iq.attrib['from'] = self.xmpp.fulljid
|
||||
iq.attrib['from'] = self.xmpp.boundjid.full
|
||||
id = iq['id']
|
||||
result = iq.send()
|
||||
if result is None or result == False or result['type'] == 'error':
|
||||
|
@ -156,7 +156,7 @@ class xep_0060(base.base_plugin):
|
|||
iq = self.xmpp.makeIqGet()
|
||||
iq.append(pubsub)
|
||||
iq.attrib['to'] = jid
|
||||
iq.attrib['from'] = self.xmpp.fulljid
|
||||
iq.attrib['from'] = self.xmpp.boundjid.full
|
||||
id = iq['id']
|
||||
result = iq.send()
|
||||
if result is None or result == False or result['type'] == 'error':
|
||||
|
@ -179,7 +179,7 @@ class xep_0060(base.base_plugin):
|
|||
pubsub.append(delete)
|
||||
iq.append(pubsub)
|
||||
iq.attrib['to'] = jid
|
||||
iq.attrib['from'] = self.xmpp.fulljid
|
||||
iq.attrib['from'] = self.xmpp.boundjid.full
|
||||
result = iq.send()
|
||||
if result is not None and result is not False and result['type'] != 'error':
|
||||
return True
|
||||
|
@ -196,7 +196,7 @@ class xep_0060(base.base_plugin):
|
|||
pubsub.append(configure)
|
||||
iq = self.xmpp.makeIqSet(pubsub)
|
||||
iq.attrib['to'] = jid
|
||||
iq.attrib['from'] = self.xmpp.fulljid
|
||||
iq.attrib['from'] = self.xmpp.boundjid.full
|
||||
id = iq['id']
|
||||
result = iq.send()
|
||||
if result is None or result['type'] == 'error':
|
||||
|
@ -217,7 +217,7 @@ class xep_0060(base.base_plugin):
|
|||
pubsub.append(publish)
|
||||
iq = self.xmpp.makeIqSet(pubsub)
|
||||
iq.attrib['to'] = jid
|
||||
iq.attrib['from'] = self.xmpp.fulljid
|
||||
iq.attrib['from'] = self.xmpp.boundjid.full
|
||||
id = iq['id']
|
||||
result = iq.send()
|
||||
if result is None or result is False or result['type'] == 'error': return False
|
||||
|
@ -236,7 +236,7 @@ class xep_0060(base.base_plugin):
|
|||
pubsub.append(retract)
|
||||
iq = self.xmpp.makeIqSet(pubsub)
|
||||
iq.attrib['to'] = jid
|
||||
iq.attrib['from'] = self.xmpp.fulljid
|
||||
iq.attrib['from'] = self.xmpp.boundjid.full
|
||||
id = iq['id']
|
||||
result = iq.send()
|
||||
if result is None or result is False or result['type'] == 'error': return False
|
||||
|
@ -287,7 +287,7 @@ class xep_0060(base.base_plugin):
|
|||
pubsub.append(affs)
|
||||
iq = self.xmpp.makeIqSet(pubsub)
|
||||
iq.attrib['to'] = ps_jid
|
||||
iq.attrib['from'] = self.xmpp.fulljid
|
||||
iq.attrib['from'] = self.xmpp.boundjid.full
|
||||
id = iq['id']
|
||||
result = iq.send()
|
||||
if result is None or result is False or result['type'] == 'error':
|
||||
|
|
|
@ -42,7 +42,7 @@ class xep_0092(base.base_plugin):
|
|||
query = ET.Element('{jabber:iq:version}query')
|
||||
iq.append(query)
|
||||
iq.attrib['to'] = jid
|
||||
iq.attrib['from'] = self.xmpp.fulljid
|
||||
iq.attrib['from'] = self.xmpp.boundjid.full
|
||||
id = iq.get('id')
|
||||
result = iq.send()
|
||||
if result and result is not None and result.get('type', 'error') != 'error':
|
||||
|
|
|
@ -94,6 +94,8 @@ class XMLStream(object):
|
|||
ssl_support -- Indicates if a SSL library is available for use.
|
||||
ssl_version -- The version of the SSL protocol to use.
|
||||
Defaults to ssl.PROTOCOL_TLSv1.
|
||||
ca_certs -- File path to a CA certificate to verify the
|
||||
server's identity.
|
||||
state -- A state machine for managing the stream's
|
||||
connection state.
|
||||
stream_footer -- The start tag and any attributes for the stream's
|
||||
|
@ -163,6 +165,7 @@ class XMLStream(object):
|
|||
|
||||
self.ssl_support = SSL_SUPPORT
|
||||
self.ssl_version = ssl.PROTOCOL_TLSv1
|
||||
self.ca_certs = None
|
||||
|
||||
self.response_timeout = RESPONSE_TIMEOUT
|
||||
|
||||
|
@ -283,7 +286,15 @@ class XMLStream(object):
|
|||
self.socket.settimeout(None)
|
||||
if self.use_ssl and self.ssl_support:
|
||||
log.debug("Socket Wrapped for SSL")
|
||||
ssl_socket = ssl.wrap_socket(self.socket)
|
||||
if self.ca_certs is None:
|
||||
cert_policy = ssl.CERT_NONE
|
||||
else:
|
||||
cert_policy = ssl.CERT_REQUIRED
|
||||
|
||||
ssl_socket = ssl.wrap_socket(self.socket,
|
||||
ca_certs=self.ca_certs,
|
||||
certs_reqs=cert_policy)
|
||||
|
||||
if hasattr(self.socket, 'socket'):
|
||||
# We are using a testing socket, so preserve the top
|
||||
# layer of wrapping.
|
||||
|
@ -387,9 +398,17 @@ class XMLStream(object):
|
|||
if self.ssl_support:
|
||||
log.info("Negotiating TLS")
|
||||
log.info("Using SSL version: %s" % str(self.ssl_version))
|
||||
if self.ca_certs is None:
|
||||
cert_policy = ssl.CERT_NONE
|
||||
else:
|
||||
cert_policy = ssl.CERT_REQUIRED
|
||||
|
||||
ssl_socket = ssl.wrap_socket(self.socket,
|
||||
ssl_version=self.ssl_version,
|
||||
do_handshake_on_connect=False)
|
||||
do_handshake_on_connect=False,
|
||||
ca_certs=self.ca_certs,
|
||||
cert_reqs=cert_policy)
|
||||
|
||||
if hasattr(self.socket, 'socket'):
|
||||
# We are using a testing socket, so preserve the top
|
||||
# layer of wrapping.
|
||||
|
|
Loading…
Reference in a new issue