mirror of
https://github.com/correl/SleekXMPP.git
synced 2024-11-27 19:19:54 +00:00
* started converstion to stanza objects
This commit is contained in:
parent
a031dd24a6
commit
8854509ccd
7 changed files with 276 additions and 347 deletions
|
@ -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)
|
||||
|
|
|
@ -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."""
|
||||
|
@ -148,10 +119,16 @@ class basexmpp(object):
|
|||
def getId(self):
|
||||
return "%x".upper() % self.id
|
||||
|
||||
def send(self, data, mask=None, timeout=60):
|
||||
def sendXML(self, data, mask=None, timeout=10):
|
||||
return self.send(self.tostring(data), mask, timeout)
|
||||
|
||||
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,60 +274,20 @@ 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:
|
||||
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
|
||||
|
@ -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
|
||||
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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 __init__(self, stream, xml=None):
|
||||
self.pfrom = ''
|
||||
self.pto = ''
|
||||
StanzaBase.__init__(self, stream, xml, xmlstreammod.stanza_extensions.get('PresenceStanza', []))
|
||||
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))
|
||||
|
||||
stanzas = ({'stanza_class': PresenceStanza, 'matcher': MatchXPath('{jabber:client}presence'), 'root': True},)
|
||||
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')
|
||||
|
||||
Presence.plugin_attrib_map['error'] = Error
|
||||
Presence.plugin_tag_map["{%s}%s" % (Error.namespace, Error.name)] = Error
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,61 +101,17 @@ 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 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()
|
||||
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 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 __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
|
||||
|
@ -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] = '"'
|
||||
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))
|
||||
|
|
Loading…
Reference in a new issue