mirror of
https://github.com/correl/SleekXMPP.git
synced 2024-11-23 19:19:53 +00:00
Merge branch 'develop' into roster
Conflicts: sleekxmpp/basexmpp.py
This commit is contained in:
commit
de6170a13d
21 changed files with 291 additions and 196 deletions
|
@ -33,7 +33,7 @@ class testps(sleekxmpp.ClientXMPP):
|
|||
self.node = "pstestnode_%s"
|
||||
self.pshost = pshost
|
||||
if pshost is None:
|
||||
self.pshost = self.server
|
||||
self.pshost = self.boundjid.host
|
||||
self.nodenum = int(nodenum)
|
||||
self.leafnode = self.nodenum + 1
|
||||
self.collectnode = self.nodenum + 2
|
||||
|
|
|
@ -16,7 +16,7 @@ import sleekxmpp
|
|||
from sleekxmpp import plugins
|
||||
|
||||
import sleekxmpp.roster as roster
|
||||
from sleekxmpp.stanza import Message, Presence, Iq, Error
|
||||
from sleekxmpp.stanza import Message, Presence, Iq, Error, StreamError
|
||||
from sleekxmpp.stanza.roster import Roster
|
||||
from sleekxmpp.stanza.nick import Nick
|
||||
from sleekxmpp.stanza.htmlim import HTMLIM
|
||||
|
@ -133,6 +133,10 @@ class BaseXMPP(XMLStream):
|
|||
Callback('Presence',
|
||||
MatchXPath("{%s}presence" % self.default_ns),
|
||||
self._handle_presence))
|
||||
self.register_handler(
|
||||
Callback('Stream Error',
|
||||
MatchXPath("{%s}error" % self.stream_ns),
|
||||
self._handle_stream_error))
|
||||
|
||||
self.add_event_handler('disconnected',
|
||||
self._handle_disconnected)
|
||||
|
@ -165,6 +169,7 @@ class BaseXMPP(XMLStream):
|
|||
self.register_stanza(Message)
|
||||
self.register_stanza(Iq)
|
||||
self.register_stanza(Presence)
|
||||
self.register_stanza(StreamError)
|
||||
|
||||
# Initialize a few default stanza plugins.
|
||||
register_stanza_plugin(Iq, Roster)
|
||||
|
@ -606,6 +611,9 @@ class BaseXMPP(XMLStream):
|
|||
"""When disconnected, reset the roster"""
|
||||
self.roster = {}
|
||||
|
||||
def _handle_stream_error(self, error):
|
||||
self.event('stream_error', error)
|
||||
|
||||
def _handle_message(self, msg):
|
||||
"""Process incoming message stanzas."""
|
||||
self.event('message', msg)
|
||||
|
|
|
@ -163,11 +163,13 @@ class ClientXMPP(BaseXMPP):
|
|||
log.debug("Since no address is supplied," + \
|
||||
"attempting SRV lookup.")
|
||||
try:
|
||||
xmpp_srv = "_xmpp-client._tcp.%s" % self.server
|
||||
xmpp_srv = "_xmpp-client._tcp.%s" % self.boundjid.host
|
||||
answers = dns.resolver.query(xmpp_srv, dns.rdatatype.SRV)
|
||||
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
||||
log.debug("No appropriate SRV record found." + \
|
||||
" Using JID server name.")
|
||||
except (dns.exception.Timeout,):
|
||||
log.debug("DNS resolution timed out.")
|
||||
else:
|
||||
# Pick a random server, weighted by priority.
|
||||
|
||||
|
|
|
@ -177,7 +177,10 @@ class xep_0030(base_plugin):
|
|||
elif node is None:
|
||||
self._handlers[htype]['jid'][jid] = handler
|
||||
elif jid is None:
|
||||
jid = self.xmpp.boundjid.full
|
||||
if self.xmpp.is_component:
|
||||
jid = self.xmpp.boundjid.full
|
||||
else:
|
||||
jid = self.xmpp.boundjid.bare
|
||||
self._handlers[htype]['node'][(jid, node)] = handler
|
||||
else:
|
||||
self._handlers[htype]['node'][(jid, node)] = handler
|
||||
|
@ -342,7 +345,7 @@ class xep_0030(base_plugin):
|
|||
"""
|
||||
self._run_node_handler('del_items', jid, node, kwargs)
|
||||
|
||||
def add_item(self, jid=None, name='', node=None, subnode='', ijid=None):
|
||||
def add_item(self, jid='', name='', node=None, subnode='', ijid=None):
|
||||
"""
|
||||
Add a new item element to the given JID/node combination.
|
||||
|
||||
|
@ -356,10 +359,12 @@ class xep_0030(base_plugin):
|
|||
subnode -- Optional node for the item.
|
||||
ijid -- The JID to modify.
|
||||
"""
|
||||
if not jid:
|
||||
jid = self.xmpp.boundjid.full
|
||||
kwargs = {'ijid': jid,
|
||||
'name': name,
|
||||
'inode': subnode}
|
||||
self._run_node_handler('add_item', jid, node, kwargs)
|
||||
self._run_node_handler('add_item', ijid, node, kwargs)
|
||||
|
||||
def del_item(self, jid=None, node=None, **kwargs):
|
||||
"""
|
||||
|
@ -500,7 +505,10 @@ class xep_0030(base_plugin):
|
|||
data -- Optional, custom data to pass to the handler.
|
||||
"""
|
||||
if jid is None:
|
||||
jid = self.xmpp.boundjid.full
|
||||
if self.xmpp.is_component:
|
||||
jid = self.xmpp.boundjid.full
|
||||
else:
|
||||
jid = self.xmpp.boundjid.bare
|
||||
if node is None:
|
||||
node = ''
|
||||
|
||||
|
@ -526,8 +534,12 @@ class xep_0030(base_plugin):
|
|||
if iq['type'] == 'get':
|
||||
log.debug("Received disco info query from " + \
|
||||
"<%s> to <%s>." % (iq['from'], iq['to']))
|
||||
if self.xmpp.is_component:
|
||||
jid = iq['to'].full
|
||||
else:
|
||||
jid = iq['to'].bare
|
||||
info = self._run_node_handler('get_info',
|
||||
iq['to'].full,
|
||||
jid,
|
||||
iq['disco_info']['node'],
|
||||
iq)
|
||||
iq.reply()
|
||||
|
@ -552,8 +564,12 @@ class xep_0030(base_plugin):
|
|||
if iq['type'] == 'get':
|
||||
log.debug("Received disco items query from " + \
|
||||
"<%s> to <%s>." % (iq['from'], iq['to']))
|
||||
if self.xmpp.is_component:
|
||||
jid = iq['to'].full
|
||||
else:
|
||||
jid = iq['to'].bare
|
||||
items = self._run_node_handler('get_items',
|
||||
iq['to'].full,
|
||||
jid,
|
||||
iq['disco_items']['node'])
|
||||
iq.reply()
|
||||
if items:
|
||||
|
@ -590,3 +606,4 @@ class xep_0030(base_plugin):
|
|||
"Using default disco#info feature.")
|
||||
info.add_feature(info.namespace)
|
||||
return info
|
||||
|
||||
|
|
|
@ -247,8 +247,8 @@ class StaticDisco(object):
|
|||
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))
|
||||
node=data.get('inode', ''),
|
||||
name=data.get('name', ''))
|
||||
|
||||
def del_item(self, jid, node, data):
|
||||
"""
|
||||
|
@ -262,3 +262,4 @@ class StaticDisco(object):
|
|||
self.nodes[(jid, node)]['items'].del_item(
|
||||
data.get('ijid', ''),
|
||||
node=data.get('inode', None))
|
||||
|
||||
|
|
|
@ -316,6 +316,7 @@ class xep_0045(base.base_plugin):
|
|||
x = ET.Element('{jabber:x:data}x', type='cancel')
|
||||
query.append(x)
|
||||
iq = self.xmpp.makeIqSet(query)
|
||||
iq['to'] = room
|
||||
iq.send()
|
||||
|
||||
def setRoomConfig(self, room, config, ifrom=''):
|
||||
|
|
|
@ -36,7 +36,7 @@ class xep_0078(base.base_plugin):
|
|||
log.debug("Starting jabber:iq:auth Authentication")
|
||||
auth_request = self.xmpp.makeIqGet()
|
||||
auth_request_query = ET.Element('{jabber:iq:auth}query')
|
||||
auth_request.attrib['to'] = self.xmpp.server
|
||||
auth_request.attrib['to'] = self.xmpp.boundjid.host
|
||||
username = ET.Element('username')
|
||||
username.text = self.xmpp.username
|
||||
auth_request_query.append(username)
|
||||
|
|
|
@ -84,5 +84,5 @@ class xep_0092(base_plugin):
|
|||
result = iq.send()
|
||||
|
||||
if result and result['type'] != 'error':
|
||||
return result['software_version']._get_stanza_values()
|
||||
return result['software_version'].values
|
||||
return False
|
||||
|
|
|
@ -33,7 +33,7 @@ class xep_0199(base.base_plugin):
|
|||
|
||||
def scheduled_ping(self):
|
||||
log.debug("pinging...")
|
||||
if self.sendPing(self.xmpp.server, self.config.get('timeout', 30)) is False:
|
||||
if self.sendPing(self.xmpp.boundjid.host, self.config.get('timeout', 30)) is False:
|
||||
log.debug("Did not recieve ping back in time. Requesting Reconnect.")
|
||||
self.xmpp.reconnect()
|
||||
|
||||
|
|
|
@ -27,10 +27,12 @@ class EntityTime(ElementBase):
|
|||
interfaces = set(('tzo', 'utc'))
|
||||
sub_interfaces = set(('tzo', 'utc'))
|
||||
|
||||
#def get_utc(self): # TODO: return a datetime.tzinfo object?
|
||||
#def get_tzo(self):
|
||||
# TODO: Right now it returns a string but maybe it should
|
||||
# return a datetime.tzinfo object or maybe a datetime.timedelta?
|
||||
#pass
|
||||
|
||||
def set_tzo(self, tzo): # TODO: support datetime.tzinfo objects?
|
||||
def set_tzo(self, tzo):
|
||||
if isinstance(tzo, tzinfo):
|
||||
td = datetime.now(tzo).utcoffset() # What if we are faking the time? datetime.now() shouldn't be used here'
|
||||
seconds = td.seconds + td.days * 24 * 3600
|
||||
|
@ -45,7 +47,7 @@ class EntityTime(ElementBase):
|
|||
# Returns a datetime object instead the string. Is this a good idea?
|
||||
value = self._get_sub_text('utc')
|
||||
if '.' in value:
|
||||
return datetime.strptime(value, '%Y-%m-%d.%fT%H:%M:%SZ')
|
||||
return datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ')
|
||||
else:
|
||||
return datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ')
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
|
||||
from sleekxmpp.stanza.error import Error
|
||||
from sleekxmpp.stanza.stream_error import StreamError
|
||||
from sleekxmpp.stanza.iq import Iq
|
||||
from sleekxmpp.stanza.message import Message
|
||||
from sleekxmpp.stanza.presence import Presence
|
||||
|
|
|
@ -224,4 +224,3 @@ class Iq(RootStanza):
|
|||
else:
|
||||
StanzaBase._set_stanza_values(self, values)
|
||||
return self
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ class Nick(ElementBase):
|
|||
del_nick -- Remove the <nick> element.
|
||||
"""
|
||||
|
||||
namespace = 'http://jabber.org/nick/nick'
|
||||
namespace = 'http://jabber.org/protocol/nick'
|
||||
name = 'nick'
|
||||
plugin_attrib = name
|
||||
interfaces = set(('nick',))
|
||||
|
|
69
sleekxmpp/stanza/stream_error.py
Normal file
69
sleekxmpp/stanza/stream_error.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2010 Nathanael C. Fritz
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
|
||||
from sleekxmpp.stanza.error import Error
|
||||
from sleekxmpp.xmlstream import StanzaBase, ElementBase, ET
|
||||
from sleekxmpp.xmlstream import register_stanza_plugin
|
||||
|
||||
|
||||
class StreamError(Error, StanzaBase):
|
||||
|
||||
"""
|
||||
XMPP stanzas of type 'error' should include an <error> stanza that
|
||||
describes the nature of the error and how it should be handled.
|
||||
|
||||
Use the 'XEP-0086: Error Condition Mappings' plugin to include error
|
||||
codes used in older XMPP versions.
|
||||
|
||||
The stream:error stanza is used to provide more information for
|
||||
error that occur with the underlying XML stream itself, and not
|
||||
a particular stanza.
|
||||
|
||||
Note: The StreamError stanza is mostly the same as the normal
|
||||
Error stanza, but with different namespaces and
|
||||
condition names.
|
||||
|
||||
Example error stanza:
|
||||
<stream:error>
|
||||
<not-well-formed xmlns="urn:ietf:params:xml:ns:xmpp-streams" />
|
||||
<text xmlns="urn:ietf:params:xml:ns:xmpp-streams">
|
||||
XML was not well-formed.
|
||||
</text>
|
||||
</stream:error>
|
||||
|
||||
Stanza Interface:
|
||||
condition -- The name of the condition element.
|
||||
text -- Human readable description of the error.
|
||||
|
||||
Attributes:
|
||||
conditions -- The set of allowable error condition elements.
|
||||
condition_ns -- The namespace for the condition element.
|
||||
|
||||
Methods:
|
||||
setup -- Overrides ElementBase.setup.
|
||||
get_condition -- Retrieve the name of the condition element.
|
||||
set_condition -- Add a condition element.
|
||||
del_condition -- Remove the condition element.
|
||||
get_text -- Retrieve the contents of the <text> element.
|
||||
set_text -- Set the contents of the <text> element.
|
||||
del_text -- Remove the <text> element.
|
||||
"""
|
||||
|
||||
namespace = 'http://etherx.jabber.org/streams'
|
||||
interfaces = set(('condition', 'text'))
|
||||
conditions = set((
|
||||
'bad-format', 'bad-namespace-prefix', 'conflict',
|
||||
'connection-timeout', 'host-gone', 'host-unknown',
|
||||
'improper-addressing', 'internal-server-error', 'invalid-from',
|
||||
'invalid-namespace', 'invalid-xml', 'not-authorized',
|
||||
'not-well-formed', 'policy-violation', 'remote-connection-failed',
|
||||
'reset', 'resource-constraint', 'restricted-xml', 'see-other-host',
|
||||
'system-shutdown', 'undefined-condition', 'unsupported-encoding',
|
||||
'unsupported-feature', 'unsupported-stanza-type',
|
||||
'unsupported-version'))
|
||||
condition_ns = 'urn:ietf:params:xml:ns:xmpp-streams'
|
|
@ -183,8 +183,7 @@ class SleekTest(unittest.TestCase):
|
|||
"""
|
||||
Create and compare several stanza objects to a correct XML string.
|
||||
|
||||
If use_values is False, test using getStanzaValues() and
|
||||
setStanzaValues() will not be used.
|
||||
If use_values is False, tests using stanza.values will not be used.
|
||||
|
||||
Some stanzas provide default values for some interfaces, but
|
||||
these defaults can be problematic for testing since they can easily
|
||||
|
@ -207,9 +206,8 @@ class SleekTest(unittest.TestCase):
|
|||
values. These interfaces will be set to their
|
||||
defaults for the given and generated stanzas to
|
||||
prevent unexpected test failures.
|
||||
use_values -- Indicates if testing using getStanzaValues() and
|
||||
setStanzaValues() should be used. Defaults to
|
||||
True.
|
||||
use_values -- Indicates if testing using stanza.values should
|
||||
be used. Defaults to True.
|
||||
"""
|
||||
if method is None and hasattr(self, 'match_method'):
|
||||
method = getattr(self, 'match_method')
|
||||
|
@ -242,10 +240,10 @@ class SleekTest(unittest.TestCase):
|
|||
stanza2 = stanza_class(xml=xml)
|
||||
|
||||
if use_values:
|
||||
# Using getStanzaValues() and setStanzaValues() will add
|
||||
# XML for any interface that has a default value. We need
|
||||
# to set those defaults on the existing stanzas and XML
|
||||
# so that they will compare correctly.
|
||||
# Using stanza.values will add XML for any interface that
|
||||
# has a default value. We need to set those defaults on
|
||||
# the existing stanzas and XML so that they will compare
|
||||
# correctly.
|
||||
default_stanza = stanza_class()
|
||||
if defaults is None:
|
||||
known_defaults = {
|
||||
|
@ -264,9 +262,9 @@ class SleekTest(unittest.TestCase):
|
|||
value = default_stanza.xml.attrib[interface]
|
||||
xml.attrib[interface] = value
|
||||
|
||||
values = stanza2.getStanzaValues()
|
||||
values = stanza2.values
|
||||
stanza3 = stanza_class()
|
||||
stanza3.setStanzaValues(values)
|
||||
stanza3.values = values
|
||||
|
||||
debug = "Three methods for creating stanzas do not match.\n"
|
||||
debug += "Given XML:\n%s\n" % tostring(xml)
|
||||
|
@ -416,8 +414,7 @@ class SleekTest(unittest.TestCase):
|
|||
'id', 'stanzapath', 'xpath', and 'mask'.
|
||||
Defaults to the value of self.match_method.
|
||||
use_values -- Indicates if stanza comparisons should test using
|
||||
getStanzaValues() and setStanzaValues().
|
||||
Defaults to True.
|
||||
stanza.values. Defaults to True.
|
||||
timeout -- Time to wait in seconds for data to be received by
|
||||
a live connection.
|
||||
"""
|
||||
|
|
4
sleekxmpp/thirdparty/__init__.py
vendored
4
sleekxmpp/thirdparty/__init__.py
vendored
|
@ -0,0 +1,4 @@
|
|||
try:
|
||||
from collections import OrderedDict
|
||||
except:
|
||||
from sleekxmpp.thirdparty.ordereddict import OrderedDict
|
|
@ -14,6 +14,7 @@ from xml.etree import cElementTree as ET
|
|||
|
||||
from sleekxmpp.xmlstream import JID
|
||||
from sleekxmpp.xmlstream.tostring import tostring
|
||||
from sleekxmpp.thirdparty import OrderedDict
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -23,17 +24,23 @@ log = logging.getLogger(__name__)
|
|||
XML_TYPE = type(ET.Element('xml'))
|
||||
|
||||
|
||||
def register_stanza_plugin(stanza, plugin):
|
||||
def register_stanza_plugin(stanza, plugin, iterable=False):
|
||||
"""
|
||||
Associate a stanza object as a plugin for another stanza.
|
||||
|
||||
Arguments:
|
||||
stanza -- The class of the parent stanza.
|
||||
plugin -- The class of the plugin stanza.
|
||||
stanza -- The class of the parent stanza.
|
||||
plugin -- The class of the plugin stanza.
|
||||
iterable -- Indicates if the plugin stanza
|
||||
should be included in the parent
|
||||
stanza's iterable 'substanzas'
|
||||
interface results.
|
||||
"""
|
||||
tag = "{%s}%s" % (plugin.namespace, plugin.name)
|
||||
stanza.plugin_attrib_map[plugin.plugin_attrib] = plugin
|
||||
stanza.plugin_tag_map[tag] = plugin
|
||||
if iterable:
|
||||
stanza.plugin_iterables.add(plugin)
|
||||
|
||||
|
||||
# To maintain backwards compatibility for now, preserve the camel case name.
|
||||
|
@ -95,10 +102,22 @@ class ElementBase(object):
|
|||
>>> message['custom']['useful_thing'] = 'foo'
|
||||
|
||||
If a plugin provides an interface that is the same as the plugin's
|
||||
plugin_attrib value, then the plugin's interface may be accessed
|
||||
directly from the parent stanza, as so:
|
||||
plugin_attrib value, then the plugin's interface may be assigned
|
||||
directly from the parent stanza, as shown below, but retrieving
|
||||
information will require all interfaces to be used, as so:
|
||||
|
||||
>>> message['custom'] = 'bar' # Same as using message['custom']['custom']
|
||||
>>> message['custom']['custom'] # Must use all interfaces
|
||||
'bar'
|
||||
|
||||
If the plugin sets the value is_extension = True, then both setting
|
||||
and getting an interface value that is the same as the plugin's
|
||||
plugin_attrib value will work, as so:
|
||||
|
||||
>>> message['custom'] = 'bar' # Using is_extension=True
|
||||
>>> message['custom']
|
||||
'bar'
|
||||
|
||||
|
||||
Class Attributes:
|
||||
name -- The name of the stanza's main element.
|
||||
|
@ -108,14 +127,23 @@ class ElementBase(object):
|
|||
sub_interfaces -- A subset of the set of interfaces which map
|
||||
to subelements instead of attributes.
|
||||
subitem -- A set of stanza classes which are allowed to
|
||||
be added as substanzas.
|
||||
be added as substanzas. Deprecated version
|
||||
of plugin_iterables.
|
||||
types -- A set of generic type attribute values.
|
||||
tag -- The namespaced name of the stanza's root
|
||||
element. Example: "{foo_ns}bar"
|
||||
plugin_attrib -- The interface name that the stanza uses to be
|
||||
accessed as a plugin from another stanza.
|
||||
plugin_attrib_map -- A mapping of plugin attribute names with the
|
||||
associated plugin stanza classes.
|
||||
plugin_iterables -- A set of stanza classes which are allowed to
|
||||
be added as substanzas.
|
||||
plugin_tag_map -- A mapping of plugin stanza tag names with
|
||||
the associated plugin stanza classes.
|
||||
is_extension -- When True, allows the stanza to provide one
|
||||
additional interface to the parent stanza,
|
||||
extending the interfaces supported by the
|
||||
parent. Defaults to False.
|
||||
xml_ns -- The XML namespace,
|
||||
http://www.w3.org/XML/1998/namespace,
|
||||
for use with xml:lang values.
|
||||
|
@ -128,6 +156,10 @@ class ElementBase(object):
|
|||
values -- A dictionary of the stanza's interfaces
|
||||
and interface values, including plugins.
|
||||
|
||||
Class Methods
|
||||
tag_name -- Return the namespaced version of the stanza's
|
||||
root element's name.
|
||||
|
||||
Methods:
|
||||
setup -- Initialize the stanza's XML contents.
|
||||
enable -- Instantiate a stanza plugin.
|
||||
|
@ -160,6 +192,7 @@ class ElementBase(object):
|
|||
appendxml -- Add XML content to the stanza.
|
||||
pop -- Remove a substanza.
|
||||
next -- Return the next iterable substanza.
|
||||
clear -- Reset the stanza's XML contents.
|
||||
_fix_ns -- Apply the stanza's namespace to non-namespaced
|
||||
elements in an XPath expression.
|
||||
"""
|
||||
|
@ -171,8 +204,10 @@ class ElementBase(object):
|
|||
types = set(('get', 'set', 'error', None, 'unavailable', 'normal', 'chat'))
|
||||
sub_interfaces = tuple()
|
||||
plugin_attrib_map = {}
|
||||
plugin_iterables = set()
|
||||
plugin_tag_map = {}
|
||||
subitem = None
|
||||
is_extension = False
|
||||
xml_ns = 'http://www.w3.org/XML/1998/namespace'
|
||||
|
||||
def __init__(self, xml=None, parent=None):
|
||||
|
@ -196,9 +231,10 @@ class ElementBase(object):
|
|||
self.setStanzaValues = self._set_stanza_values
|
||||
|
||||
self.xml = xml
|
||||
self.plugins = {}
|
||||
self.plugins = OrderedDict()
|
||||
self.iterables = []
|
||||
self._index = 0
|
||||
self.tag = self.tag_name()
|
||||
if parent is None:
|
||||
self.parent = None
|
||||
else:
|
||||
|
@ -218,9 +254,11 @@ class ElementBase(object):
|
|||
self.plugins[plugin.plugin_attrib] = plugin(child, self)
|
||||
if self.subitem is not None:
|
||||
for sub in self.subitem:
|
||||
if child.tag == "{%s}%s" % (sub.namespace, sub.name):
|
||||
self.iterables.append(sub(child, self))
|
||||
break
|
||||
self.plugin_iterables.add(sub)
|
||||
for sub in self.plugin_iterables:
|
||||
if child.tag == "{%s}%s" % (sub.namespace, sub.name):
|
||||
self.iterables.append(sub(child, self))
|
||||
break
|
||||
|
||||
def setup(self, xml=None):
|
||||
"""
|
||||
|
@ -287,14 +325,12 @@ class ElementBase(object):
|
|||
for interface in self.interfaces:
|
||||
values[interface] = self[interface]
|
||||
for plugin, stanza in self.plugins.items():
|
||||
values[plugin] = stanza._get_stanza_values()
|
||||
values[plugin] = stanza.values
|
||||
if self.iterables:
|
||||
iterables = []
|
||||
for stanza in self.iterables:
|
||||
iterables.append(stanza._get_stanza_values())
|
||||
iterables[-1].update({
|
||||
'__childtag__': "{%s}%s" % (stanza.namespace,
|
||||
stanza.name)})
|
||||
iterables.append(stanza.values)
|
||||
iterables[-1]['__childtag__'] = stanza.tag
|
||||
values['substanzas'] = iterables
|
||||
return values
|
||||
|
||||
|
@ -318,7 +354,7 @@ class ElementBase(object):
|
|||
subclass.name)
|
||||
if subdict['__childtag__'] == child_tag:
|
||||
sub = subclass(parent=self)
|
||||
sub._set_stanza_values(subdict)
|
||||
sub.values = subdict
|
||||
self.iterables.append(sub)
|
||||
break
|
||||
elif interface in self.interfaces:
|
||||
|
@ -326,7 +362,7 @@ class ElementBase(object):
|
|||
elif interface in self.plugin_attrib_map:
|
||||
if interface not in self.plugins:
|
||||
self.init_plugin(interface)
|
||||
self.plugins[interface]._set_stanza_values(value)
|
||||
self.plugins[interface].values = value
|
||||
return self
|
||||
|
||||
def __getitem__(self, attrib):
|
||||
|
@ -371,6 +407,8 @@ class ElementBase(object):
|
|||
elif attrib in self.plugin_attrib_map:
|
||||
if attrib not in self.plugins:
|
||||
self.init_plugin(attrib)
|
||||
if self.plugins[attrib].is_extension:
|
||||
return self.plugins[attrib][attrib]
|
||||
return self.plugins[attrib]
|
||||
else:
|
||||
return ''
|
||||
|
@ -467,8 +505,13 @@ class ElementBase(object):
|
|||
elif attrib in self.plugin_attrib_map:
|
||||
if attrib in self.plugins:
|
||||
xml = self.plugins[attrib].xml
|
||||
if self.plugins[attrib].is_extension:
|
||||
del self.plugins[attrib][attrib]
|
||||
del self.plugins[attrib]
|
||||
self.xml.remove(xml)
|
||||
try:
|
||||
self.xml.remove(xml)
|
||||
except:
|
||||
pass
|
||||
return self
|
||||
|
||||
def _set_attr(self, name, value):
|
||||
|
@ -790,6 +833,28 @@ class ElementBase(object):
|
|||
"""
|
||||
return self.__next__()
|
||||
|
||||
def clear(self):
|
||||
"""
|
||||
Remove all XML element contents and plugins.
|
||||
|
||||
Any attribute values will be preserved.
|
||||
"""
|
||||
for child in self.xml.getchildren():
|
||||
self.xml.remove(child)
|
||||
for plugin in list(self.plugins.keys()):
|
||||
del self.plugins[plugin]
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def tag_name(cls):
|
||||
"""
|
||||
Return the namespaced name of the stanza's root element.
|
||||
|
||||
For example, for the stanza <foo xmlns="bar" />,
|
||||
stanza.tag would return "{bar}foo".
|
||||
"""
|
||||
return "{%s}%s" % (cls.namespace, cls.name)
|
||||
|
||||
@property
|
||||
def attrib(self):
|
||||
"""
|
||||
|
@ -862,13 +927,13 @@ class ElementBase(object):
|
|||
return False
|
||||
|
||||
# Check that this stanza is a superset of the other stanza.
|
||||
values = self._get_stanza_values()
|
||||
values = self.values
|
||||
for key in other.keys():
|
||||
if key not in values or values[key] != other[key]:
|
||||
return False
|
||||
|
||||
# Check that the other stanza is a superset of this stanza.
|
||||
values = other._get_stanza_values()
|
||||
values = other.values
|
||||
for key in self.keys():
|
||||
if key not in values or values[key] != self[key]:
|
||||
return False
|
||||
|
@ -972,7 +1037,6 @@ class StanzaBase(ElementBase):
|
|||
|
||||
Attributes:
|
||||
stream -- The XMLStream instance that will handle sending this stanza.
|
||||
tag -- The namespaced version of the stanza's name.
|
||||
|
||||
Methods:
|
||||
set_type -- Set the type of the stanza.
|
||||
|
@ -983,7 +1047,6 @@ class StanzaBase(ElementBase):
|
|||
get_payload -- Return the stanza's XML contents.
|
||||
set_payload -- Append to the stanza's XML contents.
|
||||
del_payload -- Remove the stanza's XML contents.
|
||||
clear -- Reset the stanza's XML contents.
|
||||
reply -- Reset the stanza and modify the 'to' and 'from'
|
||||
attributes to prepare for sending a reply.
|
||||
error -- Set the stanza's type to 'error'.
|
||||
|
@ -1098,18 +1161,6 @@ class StanzaBase(ElementBase):
|
|||
self.clear()
|
||||
return self
|
||||
|
||||
def clear(self):
|
||||
"""
|
||||
Remove all XML element contents and plugins.
|
||||
|
||||
Any attribute values will be preserved.
|
||||
"""
|
||||
for child in self.xml.getchildren():
|
||||
self.xml.remove(child)
|
||||
for plugin in list(self.plugins.keys()):
|
||||
del self.plugins[plugin]
|
||||
return self
|
||||
|
||||
def reply(self):
|
||||
"""
|
||||
Reset the stanza and swap its 'from' and 'to' attributes to prepare
|
||||
|
|
|
@ -292,6 +292,7 @@ class XMLStream(object):
|
|||
return True
|
||||
except Socket.error as serr:
|
||||
error_msg = "Could not connect to %s:%s. Socket Error #%s: %s"
|
||||
self.event('socket_error', serr)
|
||||
log.error(error_msg % (self.address[0], self.address[1],
|
||||
serr.errno, serr.strerror))
|
||||
time.sleep(1)
|
||||
|
@ -327,7 +328,7 @@ class XMLStream(object):
|
|||
self.filesocket.close()
|
||||
self.socket.shutdown(Socket.SHUT_RDWR)
|
||||
except Socket.error as serr:
|
||||
pass
|
||||
self.event('socket_error', serr)
|
||||
finally:
|
||||
#clear your application state
|
||||
self.event("disconnected", direct=True)
|
||||
|
@ -734,7 +735,8 @@ class XMLStream(object):
|
|||
except SystemExit:
|
||||
log.debug("SystemExit in _process")
|
||||
self.stop.set()
|
||||
except Socket.error:
|
||||
except Socket.error as serr:
|
||||
self.event('socket_error', serr)
|
||||
log.exception('Socket Error')
|
||||
except:
|
||||
if not self.stop.isSet():
|
||||
|
@ -800,7 +802,8 @@ class XMLStream(object):
|
|||
default_ns = self.default_ns
|
||||
stanza_type = StanzaBase
|
||||
for stanza_class in self.__root_stanza:
|
||||
if xml.tag == "{%s}%s" % (default_ns, stanza_class.name):
|
||||
if xml.tag == "{%s}%s" % (default_ns, stanza_class.name) or \
|
||||
xml.tag == stanza_class.tag_name():
|
||||
stanza_type = stanza_class
|
||||
break
|
||||
stanza = stanza_type(self, xml)
|
||||
|
@ -825,7 +828,8 @@ class XMLStream(object):
|
|||
# stanza type applies, a generic StanzaBase stanza will be used.
|
||||
stanza_type = StanzaBase
|
||||
for stanza_class in self.__root_stanza:
|
||||
if xml.tag == "{%s}%s" % (self.default_ns, stanza_class.name):
|
||||
if xml.tag == "{%s}%s" % (self.default_ns, stanza_class.name) or \
|
||||
xml.tag == stanza_class.tag_name():
|
||||
stanza_type = stanza_class
|
||||
break
|
||||
stanza = stanza_type(self, xml)
|
||||
|
@ -899,7 +903,7 @@ class XMLStream(object):
|
|||
args[0].exception(e)
|
||||
elif etype == 'schedule':
|
||||
try:
|
||||
log.debug(args)
|
||||
log.debug('Scheduled event: %s' % args)
|
||||
handler(*args[0])
|
||||
except:
|
||||
log.exception('Error processing scheduled task')
|
||||
|
|
|
@ -49,7 +49,7 @@ class TestMessageStanzas(SleekTest):
|
|||
msg['nick']['nick'] = 'A nickname!'
|
||||
self.check(msg, """
|
||||
<message>
|
||||
<nick xmlns="http://jabber.org/nick/nick">A nickname!</nick>
|
||||
<nick xmlns="http://jabber.org/protocol/nick">A nickname!</nick>
|
||||
</message>
|
||||
""")
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ class TestPresenceStanzas(SleekTest):
|
|||
p['nick']['nick'] = 'A nickname!'
|
||||
self.check(p, """
|
||||
<presence>
|
||||
<nick xmlns="http://jabber.org/nick/nick">A nickname!</nick>
|
||||
<nick xmlns="http://jabber.org/protocol/nick">A nickname!</nick>
|
||||
</presence>
|
||||
""")
|
||||
|
||||
|
|
185
todo1.0
185
todo1.0
|
@ -1,123 +1,62 @@
|
|||
ElementBase sub_items not subitem?
|
||||
|
||||
*XMPP needs to use JID class instead of lots of fields.
|
||||
|
||||
BaseXMPP set_jid, makeIqQuery, getjidresource, getjidbare not needed
|
||||
|
||||
Why CamelCase and underscore_names? Document semantics.
|
||||
|
||||
conn_tests and sleekxmpp/tests and sleekxmpp/xmlstresm/test.* -> convert to either unit tests, or at least put in same place
|
||||
|
||||
Update setup.py - github url, version #
|
||||
|
||||
scheduler needs unit tests
|
||||
|
||||
ClientXMPP stream:features handler should use new state machine
|
||||
|
||||
Write stream tests for startls, features, etc.
|
||||
|
||||
|
||||
|
||||
-- PEP8 - all files
|
||||
|
||||
Need to use spaces
|
||||
|
||||
Docstrings are lacking. Need to document attributes and return values.
|
||||
|
||||
Organize imports
|
||||
|
||||
Use absolute, not relative imports
|
||||
|
||||
Fix one-liner if statements
|
||||
|
||||
Line length limit of 79 characters
|
||||
|
||||
|
||||
|
||||
-- Plugins
|
||||
|
||||
--- xep_0004
|
||||
|
||||
Need more unit tests
|
||||
|
||||
--- xep_0009
|
||||
|
||||
Need stanza objects
|
||||
|
||||
Need unit tests
|
||||
|
||||
--- xep_0045
|
||||
|
||||
Need to use stanza objects
|
||||
|
||||
A few TODO comments for checking roles and using defaults
|
||||
|
||||
Need unit tests
|
||||
|
||||
--- xep_0050
|
||||
|
||||
Need unit tests
|
||||
|
||||
Need stanza objects - use new xep_0004
|
||||
|
||||
--- xep_0060
|
||||
|
||||
Need unit tests
|
||||
|
||||
Need to use existing stanza objects
|
||||
|
||||
--- xep_0078
|
||||
|
||||
Is it useful still?
|
||||
|
||||
Need stanza objects/unit tests
|
||||
|
||||
--- xep_0086
|
||||
|
||||
Is there a way to automate setting error codes?
|
||||
|
||||
Seems like this should be part of the error stanza by default
|
||||
|
||||
Use stanza objects
|
||||
|
||||
--- xep_0092
|
||||
|
||||
Stanza objects
|
||||
|
||||
Unit tests
|
||||
|
||||
--- xep_0199
|
||||
|
||||
Stanza objects
|
||||
|
||||
Unit tests
|
||||
|
||||
Clean commented code
|
||||
|
||||
Use the new scheduler
|
||||
|
||||
|
||||
|
||||
-- Documentation
|
||||
|
||||
Document the Zen/Tao/Whatever of SleekXMPP to explain design goals and decisions
|
||||
|
||||
Write architecture description
|
||||
|
||||
XMPP:TDG needs to be rewritten.
|
||||
|
||||
Need to update docs that reference old JID attributes of sleekxmpp objects
|
||||
|
||||
Page describing new JID class
|
||||
|
||||
Message page needs updating
|
||||
|
||||
Iq page needs to be written
|
||||
|
||||
Make guides to go with example.py and component_example.py
|
||||
|
||||
Page on xmlstream.matchers
|
||||
|
||||
Page on xmlstream.handlers, especially waiters
|
||||
|
||||
Page on using xmlstream.scheduler
|
||||
Plugins:
|
||||
0004
|
||||
PEP8
|
||||
Stream/Unit tests
|
||||
Fix serialization issue
|
||||
Use OrderedDict for fields/values
|
||||
0009
|
||||
Review contribution from dannmartens
|
||||
0012
|
||||
PEP8
|
||||
Documentation
|
||||
Stream/Unit tests
|
||||
0030
|
||||
Done
|
||||
0033
|
||||
PEP8
|
||||
Documentation
|
||||
Stream/Unit tests
|
||||
0045
|
||||
PEP8
|
||||
Documentation
|
||||
Stream/Unit tests
|
||||
0050
|
||||
Review replacement in github.com/legastero/adhoc
|
||||
0059
|
||||
Done
|
||||
0060
|
||||
PEP8
|
||||
Documentation
|
||||
Stream/Unit tests
|
||||
0078
|
||||
Will require new stream features handling, see stream_features branch.
|
||||
PEP8
|
||||
Documentation
|
||||
Stream/Unit tests
|
||||
0085
|
||||
PEP8
|
||||
Documentation
|
||||
Stream/Unit tests
|
||||
0086
|
||||
PEP8
|
||||
Documentation
|
||||
Consider any simplifications.
|
||||
0092
|
||||
Done
|
||||
0128
|
||||
Needs complete rewrite to work with new 0030 plugin.
|
||||
0199
|
||||
PEP8
|
||||
Documentation
|
||||
Stream/Unit tests
|
||||
Needs to use scheduler instead of its own thread.
|
||||
0202
|
||||
PEP8
|
||||
Documentation
|
||||
Stream/Unit tests
|
||||
0249
|
||||
Review, minor cleanup
|
||||
gmail_notify
|
||||
PEP8
|
||||
Documentation
|
||||
Stream/Unit tests
|
||||
|
|
Loading…
Reference in a new issue