summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Fritz <fritzy@netflint.net>2009-12-11 01:29:46 +0000
committerNathan Fritz <fritzy@netflint.net>2009-12-11 01:29:46 +0000
commit8854509ccdbc1f9ea74e1eb00f3af098f2a3b6b6 (patch)
tree0cd10d2532fe57cf49128779bbc2375875afb8b0
parenta031dd24a6c82745b7272d98229ebc69dd5f4811 (diff)
downloadslixmpp-8854509ccdbc1f9ea74e1eb00f3af098f2a3b6b6.tar.gz
slixmpp-8854509ccdbc1f9ea74e1eb00f3af098f2a3b6b6.tar.bz2
slixmpp-8854509ccdbc1f9ea74e1eb00f3af098f2a3b6b6.tar.xz
slixmpp-8854509ccdbc1f9ea74e1eb00f3af098f2a3b6b6.zip
* started converstion to stanza objects
-rw-r--r--sleekxmpp/__init__.py71
-rw-r--r--sleekxmpp/basexmpp.py237
-rw-r--r--sleekxmpp/stanza/iq.py20
-rw-r--r--sleekxmpp/stanza/message.py21
-rw-r--r--sleekxmpp/stanza/presence.py62
-rw-r--r--sleekxmpp/xmlstream/handler/waiter.py3
-rw-r--r--sleekxmpp/xmlstream/stanzabase.py193
7 files changed, 268 insertions, 339 deletions
diff --git a/sleekxmpp/__init__.py b/sleekxmpp/__init__.py
index f0be089c..91dffa04 100644
--- a/sleekxmpp/__init__.py
+++ b/sleekxmpp/__init__.py
@@ -75,6 +75,7 @@ class ClientXMPP(basexmpp, XMLStream):
#self.map_namespace('http://etherx.jabber.org/streams', 'stream')
#self.map_namespace('jabber:client', '')
self.features = []
+ #TODO: Use stream state here
self.authenticated = False
self.sessionstarted = False
self.registerHandler(Callback('Stream Features', MatchXPath('{http://etherx.jabber.org/streams}features'), self._handleStreamFeatures, thread=True))
@@ -88,14 +89,6 @@ class ClientXMPP(basexmpp, XMLStream):
#self.registerStanzaExtension('PresenceStanza', PresenceStanzaType)
#self.register_plugins()
- def importStanzas(self):
- pass
- return
- for modname in stanza.__all__:
- __import__("%s.%s" % (globals()['stanza'].__name__, modname))
- for register in getattr(stanza, modname).stanzas:
- self.registerStanza(**register)
-
def __getitem__(self, key):
if key in self.plugin:
return self.plugin[key]
@@ -109,7 +102,6 @@ class ClientXMPP(basexmpp, XMLStream):
def connect(self, address=tuple()):
"""Connect to the Jabber Server. Attempts SRV lookup, and if it fails, uses
the JID server."""
- self.importStanzas()
if not address or len(address) < 2:
if not self.srvsupport:
logging.debug("Did not supply (address, port) to connect to and no SRV support is installed (http://www.dnspython.org). Continuing to attempt connection, using server hostname from JID.")
@@ -163,26 +155,15 @@ class ClientXMPP(basexmpp, XMLStream):
def updateRoster(self, jid, name=None, subscription=None, groups=[]):
"""Add or change a roster item."""
- iq = self.makeIqSet()
- iq.attrib['from'] = self.fulljid
- query = self.makeQueryRoster(iq)
- item = ET.Element('item')
- item.attrib['jid'] = jid
- if name:
- item.attrib['name'] = name
- if subscription in ['to', 'from', 'both']:
- item.attrib['subscription'] = subscription
- else:
- item.attrib['subscription'] = 'none'
- for group in groups:
- groupxml = ET.Element('group')
- groupxml.text = group
- item.append.groupxml
- return self.send(iq, self.makeIq(self.getId()))
+ iq = self.Iq().setValues({'type': 'set'})
+ iq['roster'] = {jid: {'name': name, 'subscription': subscription, 'groups': groups}}
+ #self.send(iq, self.Iq().setValues({'id': iq['id']}))
+ r = iq.send()
+ return r['type'] == 'result'
def getRoster(self):
"""Request the roster be sent."""
- self.send(self.makeIqGet('jabber:iq:roster'))
+ self.Iq().setValues({'type': 'get'}).enable('roster').send()
def _handleStreamFeatures(self, features):
self.features = []
@@ -198,7 +179,7 @@ class ClientXMPP(basexmpp, XMLStream):
def handler_starttls(self, xml):
if not self.authenticated and self.ssl_support:
self.add_handler("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls' />", self.handler_tls_start, instream=True)
- self.send(xml)
+ self.sendXML(xml)
return True
else:
logging.warning("The module tlslite is required in to some servers, and has not been found.")
@@ -240,14 +221,14 @@ class ClientXMPP(basexmpp, XMLStream):
def handler_bind_resource(self, xml):
logging.debug("Requesting resource: %s" % self.resource)
- out = self.makeIqSet()
+ iq = self.Iq(stype='set')
res = ET.Element('resource')
res.text = self.resource
xml.append(res)
- out.append(xml)
- id = out.get('id')
- response = self.send(out, self.makeIqResult(id))
- self.set_jid(response.find('{urn:ietf:params:xml:ns:xmpp-bind}bind/{urn:ietf:params:xml:ns:xmpp-bind}jid').text)
+ iq.append(xml)
+ response = iq.send()
+ #response = self.send(iq, self.Iq(sid=iq['id']))
+ self.set_jid(response.xml.find('{urn:ietf:params:xml:ns:xmpp-bind}bind/{urn:ietf:params:xml:ns:xmpp-bind}jid').text)
logging.info("Node set to: %s" % self.fulljid)
if "{urn:ietf:params:xml:ns:xmpp-session}session" not in self.features:
logging.debug("Established Session")
@@ -261,21 +242,11 @@ class ClientXMPP(basexmpp, XMLStream):
self.sessionstarted = True
self.event("session_start")
- def _handleRoster(self, roster):
- xml = roster.xml
- xml = roster.xml
- roster_update = {}
- for item in xml.findall('{jabber:iq:roster}query/{jabber:iq:roster}item'):
- if not item.attrib['jid'] in self.roster:
- self.roster[item.attrib['jid']] = {'groups': [], 'name': '', 'subscription': 'none', 'presence': {}, 'in_roster': False}
- self.roster[item.attrib['jid']]['name'] = item.get('name', '')
- self.roster[item.attrib['jid']]['subscription'] = item.get('subscription', 'none')
- self.roster[item.attrib['jid']]['in_roster'] = 'True'
- for group in item.findall('{jabber:iq:roster}group'):
- self.roster[item.attrib['jid']]['groups'].append(group.text)
- if self.roster[item.attrib['jid']]['groups'] == []:
- self.roster[item.attrib['jid']]['groups'].append('Default')
- roster_update[item.attrib['jid']] = self.roster[item.attrib['jid']]
- if xml.get('type', 'result') == 'set':
- self.send(self.makeIqResult(xml.get('id', '0')))
- self.event("roster_update", roster_update)
+ def _handleRoster(self, iq):
+ for jid in iq['roster']['items']:
+ if not jid.bare in self.roster:
+ self.roster[jid.bare] = {'groups': [], 'name': '', 'subscription': 'none', 'presence': {}, 'in_roster': True}
+ self.roster[jid.bare].update(iq['roster']['jid'])
+ if iq['type'] == 'set':
+ self.send(self.Iq().setValues({'type': 'result', 'id': iq['id']}).enable('roster'))
+ self.event("roster_update", iq)
diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py
index 0eeb52e8..2288e8e7 100644
--- a/sleekxmpp/basexmpp.py
+++ b/sleekxmpp/basexmpp.py
@@ -28,61 +28,22 @@ from . xmlstream.handler.callback import Callback
from . import plugins
from . stanza.message import Message
from . stanza.iq import Iq
+from . stanza.presence import Presence
+from . stanza.roster import Roster
import logging
import threading
+def stanzaPlugin(stanza, plugin):
+ stanza.plugin_attrib_map[plugin.plugin_attrib] = plugin
+ stanza.plugin_tag_map["{%s}%s" % (plugin.namespace, plugin.name)] = plugin
+
+stanzaPlugin(Iq, Roster)
+
class basexmpp(object):
def __init__(self):
self.id = 0
self.id_lock = threading.Lock()
- self.stanza_errors = {
- 'bad-request':False,
- 'conflict':False,
- 'feature-not-implemented':False,
- 'forbidden':False,
- 'gone':True,
- 'internal-server-error':False,
- 'item-not-found':False,
- 'jid-malformed':False,
- 'not-acceptable':False,
- 'not-allowed':False,
- 'payment-required':False,
- 'recipient-unavailable':False,
- 'redirect':True,
- 'registration-required':False,
- 'remote-server-not-found':False,
- 'remote-server-timeout':False,
- 'resource-constraint':False,
- 'service-unavailable':False,
- 'subscription-required':False,
- 'undefined-condition':False,
- 'unexpected-request':False}
- self.stream_errors = {
- 'bad-format':False,
- 'bad-namespace-prefix':False,
- 'conflict':False,
- 'connection-timeout':False,
- 'host-gone':False,
- 'host-unknown':False,
- 'improper-addressing':False,
- 'internal-server-error':False,
- 'invalid-from':False,
- 'invalid-id':False,
- 'invalid-namespace':False,
- 'invalid-xml':False,
- 'not-authorized':False,
- 'policy-violation':False,
- 'remote-connection-failed':False,
- 'resource-constraint':False,
- 'restricted-xml':False,
- 'see-other-host':True,
- 'system-shutdown':False,
- 'undefined-condition':False,
- 'unsupported-encoding':False,
- 'unsupported-stanza-type':False,
- 'unsupported-version':False,
- 'xml-not-well-formed':False}
self.sentpresence = False
self.fulljid = ''
self.resource = ''
@@ -94,11 +55,21 @@ class basexmpp(object):
self.auto_subscribe = True
self.event_handlers = {}
self.roster = {}
- self.registerHandler(Callback('IM', MatchMany((MatchXMLMask("<message xmlns='%s' type='chat'><body /></message>" % self.default_ns),MatchXMLMask("<message xmlns='%s' type='normal'><body /></message>" % self.default_ns),MatchXMLMask("<message xmlns='%s' type='__None__'><body /></message>" % self.default_ns))), self._handleMessage, thread=False))
- self.registerHandler(Callback('Presence', MatchMany((MatchXMLMask("<presence xmlns='%s' type='available'/>" % self.default_ns),MatchXMLMask("<presence xmlns='%s' type='__None__'/>" % self.default_ns),MatchXMLMask("<presence xmlns='%s' type='unavailable'/>" % self.default_ns))), self._handlePresence, thread=False))
- self.registerHandler(Callback('PresenceSubscribe', MatchMany((MatchXMLMask("<presence xmlns='%s' type='subscribe'/>" % self.default_ns),MatchXMLMask("<presence xmlns='%s' type='unsubscribed'/>" % self.default_ns))), self._handlePresenceSubscribe))
+ self.registerHandler(Callback('IM', MatchXMLMask("<message xmlns='%s'><body /></message>" % self.default_ns), self._handleMessage))
+ self.registerHandler(Callback('Presence', MatchXMLMask("<presence xmlns='%s' />" % self.default_ns), self._handlePresence))
+ self.add_event_handler('presence_subscribe', self._handlePresenceSubscribe)
self.registerStanza(Message)
self.registerStanza(Iq)
+ self.registerStanza(Presence)
+
+ def Message(self, *args, **kwargs):
+ return Presence(self, *args, **kwargs)
+
+ def Iq(self, *args, **kwargs):
+ return Iq(self, *args, **kwargs)
+
+ def Presence(self, *args, **kwargs):
+ return Presence(self, *args, **kwargs)
def set_jid(self, jid):
"""Rip a JID apart and claim it as our own."""
@@ -147,11 +118,17 @@ class basexmpp(object):
def getId(self):
return "%x".upper() % self.id
+
+ def sendXML(self, data, mask=None, timeout=10):
+ return self.send(self.tostring(data), mask, timeout)
- def send(self, data, mask=None, timeout=60):
+ def send(self, data, mask=None, timeout=10):
#logging.warning("Deprecated send used for \"%s\"" % (data,))
- if not type(data) == type(''):
- data = self.tostring(data)
+ #if not type(data) == type(''):
+ # data = self.tostring(data)
+ if hasattr(mask, 'xml'):
+ mask = mask.xml
+ data = str(data)
if mask is not None:
waitfor = XMLWaiter('SendWait_%s' % self.getNewId(), MatchXMLMask(mask))
self.registerHandler(waitfor)
@@ -160,86 +137,28 @@ class basexmpp(object):
return waitfor.wait(timeout)
def makeIq(self, id=0, ifrom=None):
- iq = ET.Element('{%s}iq' % self.default_ns)
- if id == 0:
- id = self.getNewId()
- iq.set('id', str(id))
- if ifrom is not None:
- iq.attrib['from'] = ifrom
- return iq
+ return self.Iq().setValues({'id': id, 'from': ifrom})
def makeIqGet(self, queryxmlns = None):
- iq = self.makeIq()
- iq.set('type', 'get')
+ iq = self.Iq().setValues({'type': 'get'})
if queryxmlns:
- query = ET.Element("{%s}query" % queryxmlns)
- iq.append(query)
+ iq.append(ET.Element("{%s}query" % queryxmlns))
return iq
def makeIqResult(self, id):
- iq = self.makeIq(id)
- iq.set('type', 'result')
- return iq
+ return self.Iq().setValues({'id': id, 'type': 'result'})
def makeIqSet(self, sub=None):
- iq = self.makeIq()
- iq.set('type', 'set')
+ iq = self.Iq().setValues({'type': 'set'})
if sub != None:
iq.append(sub)
return iq
- def makeIqError(self, id):
- iq = self.makeIq(id)
- iq.set('type', 'error')
+ def makeIqError(self, id, type='cancel', condition='feature-not-implemented', text=None):
+ iq = self.Iq().setValues({'id': id})
+ iq['error'].setValues({'type': type, 'condition': condition, 'text': text})
return iq
- def makeStanzaErrorCondition(self, condition, cdata=None):
- if condition not in self.stanza_errors:
- raise ValueError()
- stanzaError = ET.Element('{urn:ietf:params:xml:ns:xmpp-stanzas}'+condition)
- if cdata is not None:
- if not self.stanza_errors[condition]:
- raise ValueError()
- stanzaError.text = cdata
- return stanzaError
-
-
- def makeStanzaError(self, condition, errorType, code=None, text=None, customElem=None):
- if errorType not in ['auth', 'cancel', 'continue', 'modify', 'wait']:
- raise ValueError()
- error = ET.Element('error')
- error.append(self.makeStanzaErrorCondition(condition))
- error.set('type',errorType)
- if code is not None:
- error.set('code', code)
- if text is not None:
- textElem = ET.Element('text')
- textElem.text = text
- error.append(textElem)
- if customElem is not None:
- error.append(customElem)
- return error
-
- def makeStreamErrorCondition(self, condition, cdata=None):
- if condition not in self.stream_errors:
- raise ValueError()
- streamError = ET.Element('{urn:ietf:params:xml:ns:xmpp-streams}'+condition)
- if cdata is not None:
- if not self.stream_errors[condition]:
- raise ValueError()
- textElem = ET.Element('text')
- textElem.text = text
- streamError.append(textElem)
-
- def makeStreamError(self, errorElem, text=None):
- error = ET.Element('error')
- error.append(errorElem)
- if text is not None:
- textElem = ET.Element('text')
- textElem.text = text
- error.append(text)
- return error
-
def makeIqQuery(self, iq, xmlns):
query = ET.Element("{%s}query" % xmlns)
iq.append(query)
@@ -355,61 +274,21 @@ class basexmpp(object):
def _handleMessage(self, msg):
self.event('message', msg)
- #xml = msg.xml
- #ns = xml.tag.split('}')[0]
- #if ns == 'message':
- # ns = ''
- #else:
- # ns = "%s}" % ns
- #mfrom = xml.attrib['from']
- #message = xml.find('%sbody' % ns).text
- #subject = xml.find('%ssubject' % ns)
- #if subject is not None:
- # subject = subject.text
- #else:
- # subject = ''
- #resource = self.getjidresource(mfrom)
- #mfrom = self.getjidbare(mfrom)
- #mtype = xml.attrib.get('type', 'normal')
- #name = self.roster.get('name', '')
- #self.event("message", {'jid': mfrom, 'resource': resource, 'name': name, 'type': mtype, 'subject': subject, 'message': message, 'to': xml.attrib.get('to', '')})
-
def _handlePresence(self, presence):
- xml = presence.xml
- ns = xml.tag.split('}')[0]
- if ns == 'presence':
- ns = ''
- else:
- ns = "%s}" % ns
"""Update roster items based on presence"""
- show = xml.find('%sshow' % ns)
- status = xml.find('%sstatus' % ns)
- priority = xml.find('%spriority' % ns)
- fulljid = xml.attrib['from']
- to = xml.attrib['to']
- resource = self.getjidresource(fulljid)
- if not resource:
- resouce = None
- jid = self.getjidbare(fulljid)
- if type(status) == type(None) or status.text is None:
- status = ''
- else:
- status = status.text
- if type(show) == type(None):
- show = 'available'
- else:
- show = show.text
- if xml.get('type', None) == 'unavailable':
- show = 'unavailable'
- if type(priority) == type(None):
- priority = 0
- else:
- priority = int(priority.text)
+ self.event("presence_%s" % presence['type'], presence)
+ if not presence['type'] in ('available', 'unavailable'):
+ return
+ jid = presence['from'].bare
+ resource = presence['from'].resource
+ show = presence['type']
+ status = presence['status']
+ priority = presence['priority']
wasoffline = False
oldroster = self.roster.get(jid, {}).get(resource, {})
- if not jid in self.roster:
- self.roster[jid] = {'groups': [], 'name': '', 'subscription': 'none', 'presence': {}, 'in_roster': False}
+ if not presence['from'].bare in self.roster:
+ self.roster[jid] = {'groups': [], 'name': '', 'subscription': 'none', 'presence': {}, 'in_roster': False}
if not resource in self.roster[jid]['presence']:
wasoffline = True
self.roster[jid]['presence'][resource] = {'show': show, 'status': status, 'priority': priority}
@@ -417,12 +296,10 @@ class basexmpp(object):
if self.roster[jid]['presence'][resource].get('show', None) == 'unavailable':
wasoffline = True
self.roster[jid]['presence'][resource] = {'show': show, 'status': status}
- if priority:
- self.roster[jid]['presence'][resource]['priority'] = priority
+ self.roster[jid]['presence'][resource]['priority'] = priority
name = self.roster[jid].get('name', '')
- eventdata = {'jid': jid, 'to': to, 'resource': resource, 'name': name, 'type': show, 'priority': priority, 'message': status}
- if wasoffline and show in ('available', 'away', 'xa', 'na'):
- self.event("got_online", eventdata)
+ if wasoffline and show in ('available', 'away', 'xa', 'na', 'ffc'):
+ self.event("got_online", presence)
elif not wasoffline and show == 'unavailable':
self.event("got_offline", eventdata)
if len(self.roster[jid]['presence']) > 1:
@@ -430,7 +307,7 @@ class basexmpp(object):
else:
del self.roster[jid]
elif oldroster != self.roster.get(jid, {'presence': {}})['presence'].get(resource, {}) and show != 'unavailable':
- self.event("changed_status", eventdata)
+ self.event("changed_status", presence)
name = ''
if name:
name = "(%s) " % name
@@ -438,13 +315,9 @@ class basexmpp(object):
def _handlePresenceSubscribe(self, presence):
"""Handling subscriptions automatically."""
- xml = presence.xml
if self.auto_authorize == True:
- #self.updateRoster(self.getjidbare(xml.attrib['from']))
- self.send(self.makePresence(ptype='subscribed', pto=self.getjidbare(xml.attrib['from'])))
+ self.send(self.makePresence(ptype='subscribed', pto=presence['from'].bare))
if self.auto_subscribe:
- self.send(self.makePresence(ptype='subscribe', pto=self.getjidbare(xml.attrib['from'])))
+ self.send(self.makePresence(ptype='subscribe', pto=presence['from'].bare))
elif self.auto_authorize == False:
- self.send(self.makePresence(ptype='unsubscribed', pto=self.getjidbare(xml.attrib['from'])))
- elif self.auto_authorize == None:
- pass
+ self.send(self.makePresence(ptype='unsubscribed', pto=presence['from'].bare))
diff --git a/sleekxmpp/stanza/iq.py b/sleekxmpp/stanza/iq.py
index eece37bd..68a429e0 100644
--- a/sleekxmpp/stanza/iq.py
+++ b/sleekxmpp/stanza/iq.py
@@ -1,5 +1,8 @@
from .. xmlstream.stanzabase import StanzaBase
from xml.etree import cElementTree as ET
+from . error import Error
+from .. xmlstream.handler.waiter import Waiter
+from .. xmlstream.matcher.id import MatcherId
class Iq(StanzaBase):
interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject'))
@@ -11,7 +14,6 @@ class Iq(StanzaBase):
StanzaBase.__init__(self, *args, **kwargs)
if self['id'] == '':
self['id'] = self.stream.getId()
- print("________LOADED IQ CLASS")
def result(self):
self['type'] = 'result'
@@ -37,3 +39,19 @@ class Iq(StanzaBase):
def unhandled(self):
pass
# returned unhandled error
+
+ def exception(self, traceback=None):
+ pass
+
+ def send(self, block=True, timeout=10):
+ if block:
+ waitfor = Waiter('IqWait_%s' % self['id'], MatcherId(self['id']))
+ self.stream.registerHandler(waitfor)
+ StanzaBase.send(self)
+ return waitfor.wait(timeout)
+ else:
+ return StanzaBase.send(self)
+
+
+Iq.plugin_attrib_map['error'] = Error
+Iq.plugin_tag_map["{%s}%s" % (Error.namespace, Error.name)] = Error
diff --git a/sleekxmpp/stanza/message.py b/sleekxmpp/stanza/message.py
index 3f9eb2ba..999f7924 100644
--- a/sleekxmpp/stanza/message.py
+++ b/sleekxmpp/stanza/message.py
@@ -1,5 +1,6 @@
from .. xmlstream.stanzabase import StanzaBase
from xml.etree import cElementTree as ET
+from . error import Error
class Message(StanzaBase):
interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject'))
@@ -25,21 +26,5 @@ class Message(StanzaBase):
self['body'] = body
return self
-if __name__ == '__main__':
- m = Message()
- m['to'] = 'me'
- m['from'] = 'you'
- m['type'] = 'chat'
- m.reply()
- m['body'] = 'Hello there!'
- m['subject'] = 'whatever'
- m['id'] = 'abc'
- print(str(m))
- print(m['body'])
- print(m['subject'])
- print(m['id'])
- m['type'] = None
- m['body'] = None
- m['id'] = None
- print(str(m))
- print(m['type'])
+Message.plugin_attrib_map['error'] = Error
+Message.plugin_tag_map["{%s}%s" % (Error.namespace, Error.name)] = Error
diff --git a/sleekxmpp/stanza/presence.py b/sleekxmpp/stanza/presence.py
index 0733e9a9..f591211b 100644
--- a/sleekxmpp/stanza/presence.py
+++ b/sleekxmpp/stanza/presence.py
@@ -1,21 +1,51 @@
from .. xmlstream.stanzabase import StanzaBase
-from .. xmlstream import xmlstream as xmlstreammod
-from .. xmlstream.matcher.xpath import MatchXPath
+from xml.etree import cElementTree as ET
+from . error import Error
-#_bases = [StanzaBase] + xmlstreammod.stanza_extensions.get('PresenceStanza', [])
+class Presence(StanzaBase):
+ interfaces = set(('type', 'to', 'from', 'id', 'status', 'priority'))
+ types = set(('available', 'unavailable', 'error', 'probe', 'subscribe', 'subscribed', 'unsubscribe', 'unsubscribed'))
+ showtypes = set(('dnd', 'ffc', 'xa', 'away'))
+ sub_interfaces = set(('status', 'priority'))
+ name = 'presence'
+ namespace = 'jabber:client'
-#class PresenceStanza(*_bases):
-class PresenceStanza(StanzaBase):
-
- def __init__(self, stream, xml=None):
- self.pfrom = ''
- self.pto = ''
- StanzaBase.__init__(self, stream, xml, xmlstreammod.stanza_extensions.get('PresenceStanza', []))
+ def getShowElement(self):
+ return self.xml.find("{%s}show" % self.namespace)
+
+ def setType(self, value):
+ if value in self.types:
+ show = self.getShowElement()
+ if value in self.types:
+ if show is not None:
+ self.xml.remove(show)
+ self._setAttr('type', value)
+ elif value in self.showtypes:
+ if show is None:
+ show = ET.Element("{%s}show" % self.namespace)
+ show.text = value
+ return self
- def fromXML(self, xml):
- StanzaBase.fromXML(self, xml)
- self.pfrom = xml.get('from')
- self.pto = xml.get('to')
- self.ptype = xml.get('type')
+ def setPriority(self, value):
+ self._setSubText('priority', str(value))
+
+ def getPriority(self):
+ p = self._getSubText('priority')
+ if not p: p = 0
+ return int(p)
+
+ def getType(self):
+ out = self._getAttr('type')
+ if not out:
+ show = self.getShowElement()
+ if show is not None:
+ out = show.text
+ if not out or out is None:
+ out = 'available'
+ return out
+
+ def delType(self):
+ self.setType('available')
-stanzas = ({'stanza_class': PresenceStanza, 'matcher': MatchXPath('{jabber:client}presence'), 'root': True},)
+Presence.plugin_attrib_map['error'] = Error
+Presence.plugin_tag_map["{%s}%s" % (Error.namespace, Error.name)] = Error
diff --git a/sleekxmpp/xmlstream/handler/waiter.py b/sleekxmpp/xmlstream/handler/waiter.py
index 57d96f9d..140ce443 100644
--- a/sleekxmpp/xmlstream/handler/waiter.py
+++ b/sleekxmpp/xmlstream/handler/waiter.py
@@ -1,6 +1,7 @@
from . import base
import queue
import logging
+from .. stanzabase import StanzaBase
class Waiter(base.BaseHandler):
@@ -19,7 +20,7 @@ class Waiter(base.BaseHandler):
return self._payload.get(True, timeout)
except queue.Empty:
logging.warning("Timed out waiting for %s" % self.name)
- return False
+ return StanzaBase(stype='error')
def checkDelete(self):
return True
diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py
index b21354e4..5403c69f 100644
--- a/sleekxmpp/xmlstream/stanzabase.py
+++ b/sleekxmpp/xmlstream/stanzabase.py
@@ -13,33 +13,53 @@ class JID(object):
return self.jid.split('@', 1)[-1].split('/', 1)[0]
elif name == 'full':
return self.jid
+ elif name == 'bare':
+ return self.jid.split('/', 1)[0]
def __str__(self):
return self.jid
-class StanzaBase(object):
+class ElementBase(object):
name = 'stanza'
+ plugin_attrib = 'plugin'
namespace = 'jabber:client'
interfaces = set(('type', 'to', 'from', 'id', 'payload'))
types = set(('get', 'set', 'error', None, 'unavailable', 'normal', 'chat'))
sub_interfaces = tuple()
+ plugin_attrib_map = {}
+ plugin_tag_map = {}
- def __init__(self, stream, xml=None, stype=None, sto=None, sfrom=None, sid=None):
- self.stream = stream
+ def __init__(self, xml=None, parent=None):
+ self.parent = parent
self.xml = xml
- if xml is None:
- self.xml = ET.Element("{%(namespace)s}%(name)s" % {'name': self.name, 'namespace': self.namespace})
- if stype is not None:
- self['type'] = stype
- if sto is not None:
- self['to'] = sto
- if sfrom is not None:
- self['from'] = sfrom
- self.tag = "{%s}%s" % (self.stream.default_ns, self.name)
-
+ self.plugins = {}
+ if not self.setup(xml) and len(self.plugin_attrib_map):
+ for child in self.xml.getchildren():
+ if child.tag in self.plugin_tag_map:
+ self.plugins[self.plugin_tag_map[child.tag].plugin_attrib] = self.plugin_tag_map[child.tag](xml=child, parent=self)
+
def match(self, xml):
return xml.tag == self.tag
+ def setup(self, xml=None):
+ if self.xml is None:
+ self.xml = xml
+ if self.xml is None:
+ self.xml = ET.Element("{%(namespace)s}%(name)s" % {'name': self.name, 'namespace': self.namespace})
+ if self.parent is not None:
+ self.parent.xml.append(self.xml)
+ return True #had to generate XML
+ else:
+ return False
+
+ def enable(self, attrib):
+ self.initPlugin(attrib)
+ return self
+
+ def initPlugin(self, attrib):
+ if attrib not in self.plugins:
+ self.plugins[attrib] = self.plugin_attrib_map[attrib](parent=self)
+
def __getitem__(self, attrib):
if attrib in self.interfaces:
if hasattr(self, "get%s" % attrib.title()):
@@ -49,11 +69,14 @@ class StanzaBase(object):
return self._getSubText(attrib)
else:
return self._getAttr(attrib)
+ elif attrib in self.plugin_attrib_map:
+ self.initPlugin(attrib)
+ return self.plugins[attrib]
else:
return ''
def __setitem__(self, attrib, value):
- if attrib.lower() in self.interfaces:
+ if attrib in self.interfaces:
if value is not None:
if hasattr(self, "set%s" % attrib.title()):
getattr(self, "set%s" % attrib.title())(value,)
@@ -64,6 +87,9 @@ class StanzaBase(object):
self._setAttr(attrib, value)
else:
self.__delitem__(attrib)
+ elif attrib in self.plugin_map:
+ self.initPlugin(attrib)
+ self.plugins[attrib].setValues(value)
return self
def __delitem__(self, attrib):
@@ -75,13 +101,90 @@ class StanzaBase(object):
return self._delSub(attrib)
else:
self._delAttr(attrib)
+ elif attrib in self.plugin_map:
+ if attrib in self.plugins:
+ del self.plugins[attrib]
+ return self
+
+ def __eq__(self, other):
+ values = self.getValues()
+ for key in other:
+ if key not in values or values[key] != other[key]:
+ return False
+ return True
+
+ def _setAttr(self, name, value):
+ self.xml.attrib[name] = value
+
+ def _delAttr(self, name):
+ if name in self.xml.attrib:
+ del self.xml.attrib[name]
+
+ def _getAttr(self, name):
+ return self.xml.attrib.get(name, '')
+
+ def _getSubText(self, name):
+ stanza = self.xml.find("{%s}%s" % (self.namespace, name))
+ if stanza is None or stanza.text is None:
+ return ''
+ else:
+ return stanza.text
+
+ def _setSubText(self, name, attrib={}, text=None):
+ stanza = self.xml.find("{%s}%s" % (self.namespace, name))
+ if stanza is None:
+ self.xml.append(ET.Element("{%s}%s" % (self.namespace, name), attrib))
+ stanza = self.xml.find("{%s}%s" % (self.namespace, name))
+ if text is not None:
+ stanza.text = text
+ return stanza
+
+ def _delSub(self, name):
+ for child in self.xml.getchildren():
+ if child.tag == "{%s}%s" % (self.namespace, name):
+ self.xml.remove(child)
+
+ def getValues(self):
+ out = {}
+ for interface in self.interfaces:
+ out[interface] = self[interface]
+ return out
+
+ def setValues(self, attrib):
+ for interface in attrib:
+ if interface in self.interfaces:
+ self[interface] = attrib[interface]
+ return self
+
+ def append(self, xml):
+ self.xml.append(xml)
return self
+
+ def __del__(self):
+ if self.parent is not None:
+ self.parent.xml.remove(self.xml)
+
+class StanzaBase(ElementBase):
+ name = 'stanza'
+ namespace = 'jabber:client'
+ interfaces = set(('type', 'to', 'from', 'id', 'payload'))
+ types = set(('get', 'set', 'error', None, 'unavailable', 'normal', 'chat'))
+ sub_interfaces = tuple()
+ def __init__(self, stream, xml=None, stype=None, sto=None, sfrom=None, sid=None):
+ self.stream = stream
+ self.namespace = stream.default_ns
+ ElementBase.__init__(self, xml)
+ if stype is not None:
+ self['type'] = stype
+ if sto is not None:
+ self['to'] = sto
+ if sfrom is not None:
+ self['from'] = sfrom
+ self.tag = "{%s}%s" % (self.stream.default_ns, self.name)
+
def setType(self, value):
if value in self.types:
- if value is None and 'type' in self.xml.attrib:
- del self.xml.attrib['type']
- elif value is not None:
self.xml.attrib['type'] = value
else:
raise ValueError
@@ -99,6 +202,8 @@ class StanzaBase(object):
def clear(self):
for child in self.xml.getchildren():
self.xml.remove(child)
+ for plugin in self.plugins:
+ del self.plugins[plugin]
def reply(self):
self['from'], self['to'] = self['to'], self['from']
@@ -120,48 +225,6 @@ class StanzaBase(object):
def setFrom(self, value):
return self._setAttr('from', str(value))
- def getValues(self):
- out = {}
- for interface in self.interfaces:
- out[interface] = self[interface]
- return out
-
- def setValues(self, attrib):
- for interface in attrib:
- if interface in self.interfaces:
- self[interface] = attrib[interface]
-
- def _setAttr(self, name, value):
- self.xml.attrib[name] = value
-
- def _delAttr(self, name):
- if name in self.xml.attrib:
- del self.xml.attrib[name]
-
- def _getAttr(self, name):
- return self.xml.attrib.get(name, '')
-
- def _getSubText(self, name):
- stanza = self.xml.find("{%s}%s" % (self.namespace, name))
- if stanza is None or stanza.text is None:
- return ''
- else:
- return stanza.text
-
- def _setSubText(self, name, attrib={}, text=None):
- stanza = self.xml.find("{%s}%s" % (self.namespace, name))
- if stanza is None:
- self.xml.append(ET.Element("{%s}%s" % (self.namespace, name), attrib))
- stanza = self.xml.find("{%s}%s" % (self.namespace, name))
- if text is not None:
- stanza.text = text
- return stanza
-
- def _delSub(self, name):
- for child in self.xml.getchildren():
- if child.tag == "{%s}%s" % (self.namespace, name):
- self.xml.remove(child)
-
def unhandled(self):
pass
@@ -226,15 +289,3 @@ class StanzaBase(object):
text[cc] = '&quot;'
cc += 1
return ''.join(text)
-
-
-if __name__ == '__main__':
- x = Stanza()
- x['from'] = 'you'
- x['to'] = 'me'
- print(x['from'], x['to'])
- x.reply()
- print(x['from'], x['to'])
- x['from'] = None
- print(x['from'], x['to'])
- print(str(x))