* started converstion to stanza objects

This commit is contained in:
Nathan Fritz 2009-12-11 01:29:46 +00:00
parent a031dd24a6
commit 8854509ccd
7 changed files with 276 additions and 347 deletions

View file

@ -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)

View file

@ -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))

View file

@ -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

View file

@ -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

View file

@ -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 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 setPriority(self, value):
self._setSubText('priority', str(value))
def __init__(self, stream, xml=None):
self.pfrom = ''
self.pto = ''
StanzaBase.__init__(self, stream, xml, xmlstreammod.stanza_extensions.get('PresenceStanza', []))
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')
def fromXML(self, xml):
StanzaBase.fromXML(self, xml)
self.pfrom = xml.get('from')
self.pto = xml.get('to')
self.ptype = xml.get('type')
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

View file

@ -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

View file

@ -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,62 +101,18 @@ class StanzaBase(object):
return self._delSub(attrib)
else:
self._delAttr(attrib)
return self
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
return self
def getPayload(self):
return self.xml.getchildren()
def setPayload(self, value):
self.xml.append(value)
def delPayload(self):
self.clear()
def clear(self):
for child in self.xml.getchildren():
self.xml.remove(child)
def reply(self):
self['from'], self['to'] = self['to'], self['from']
self.clear()
elif attrib in self.plugin_map:
if attrib in self.plugins:
del self.plugins[attrib]
return self
def error(self):
self['type'] = 'error'
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 getTo(self):
return JID(self._getAttr('to'))
def setTo(self, value):
return self._setAttr('to', str(value))
def getFrom(self):
return JID(self._getAttr('from'))
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
@ -162,6 +144,87 @@ class StanzaBase(object):
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:
self.xml.attrib['type'] = value
else:
raise ValueError
return self
def getPayload(self):
return self.xml.getchildren()
def setPayload(self, value):
self.xml.append(value)
def delPayload(self):
self.clear()
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']
self.clear()
return self
def error(self):
self['type'] = 'error'
def getTo(self):
return JID(self._getAttr('to'))
def setTo(self, value):
return self._setAttr('to', str(value))
def getFrom(self):
return JID(self._getAttr('from'))
def setFrom(self, value):
return self._setAttr('from', str(value))
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))