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('http://etherx.jabber.org/streams', 'stream')
|
||||||
#self.map_namespace('jabber:client', '')
|
#self.map_namespace('jabber:client', '')
|
||||||
self.features = []
|
self.features = []
|
||||||
|
#TODO: Use stream state here
|
||||||
self.authenticated = False
|
self.authenticated = False
|
||||||
self.sessionstarted = False
|
self.sessionstarted = False
|
||||||
self.registerHandler(Callback('Stream Features', MatchXPath('{http://etherx.jabber.org/streams}features'), self._handleStreamFeatures, thread=True))
|
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.registerStanzaExtension('PresenceStanza', PresenceStanzaType)
|
||||||
#self.register_plugins()
|
#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):
|
def __getitem__(self, key):
|
||||||
if key in self.plugin:
|
if key in self.plugin:
|
||||||
return self.plugin[key]
|
return self.plugin[key]
|
||||||
|
@ -109,7 +102,6 @@ class ClientXMPP(basexmpp, XMLStream):
|
||||||
def connect(self, address=tuple()):
|
def connect(self, address=tuple()):
|
||||||
"""Connect to the Jabber Server. Attempts SRV lookup, and if it fails, uses
|
"""Connect to the Jabber Server. Attempts SRV lookup, and if it fails, uses
|
||||||
the JID server."""
|
the JID server."""
|
||||||
self.importStanzas()
|
|
||||||
if not address or len(address) < 2:
|
if not address or len(address) < 2:
|
||||||
if not self.srvsupport:
|
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.")
|
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=[]):
|
def updateRoster(self, jid, name=None, subscription=None, groups=[]):
|
||||||
"""Add or change a roster item."""
|
"""Add or change a roster item."""
|
||||||
iq = self.makeIqSet()
|
iq = self.Iq().setValues({'type': 'set'})
|
||||||
iq.attrib['from'] = self.fulljid
|
iq['roster'] = {jid: {'name': name, 'subscription': subscription, 'groups': groups}}
|
||||||
query = self.makeQueryRoster(iq)
|
#self.send(iq, self.Iq().setValues({'id': iq['id']}))
|
||||||
item = ET.Element('item')
|
r = iq.send()
|
||||||
item.attrib['jid'] = jid
|
return r['type'] == 'result'
|
||||||
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()))
|
|
||||||
|
|
||||||
def getRoster(self):
|
def getRoster(self):
|
||||||
"""Request the roster be sent."""
|
"""Request the roster be sent."""
|
||||||
self.send(self.makeIqGet('jabber:iq:roster'))
|
self.Iq().setValues({'type': 'get'}).enable('roster').send()
|
||||||
|
|
||||||
def _handleStreamFeatures(self, features):
|
def _handleStreamFeatures(self, features):
|
||||||
self.features = []
|
self.features = []
|
||||||
|
@ -198,7 +179,7 @@ class ClientXMPP(basexmpp, XMLStream):
|
||||||
def handler_starttls(self, xml):
|
def handler_starttls(self, xml):
|
||||||
if not self.authenticated and self.ssl_support:
|
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.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
|
return True
|
||||||
else:
|
else:
|
||||||
logging.warning("The module tlslite is required in to some servers, and has not been found.")
|
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):
|
def handler_bind_resource(self, xml):
|
||||||
logging.debug("Requesting resource: %s" % self.resource)
|
logging.debug("Requesting resource: %s" % self.resource)
|
||||||
out = self.makeIqSet()
|
iq = self.Iq(stype='set')
|
||||||
res = ET.Element('resource')
|
res = ET.Element('resource')
|
||||||
res.text = self.resource
|
res.text = self.resource
|
||||||
xml.append(res)
|
xml.append(res)
|
||||||
out.append(xml)
|
iq.append(xml)
|
||||||
id = out.get('id')
|
response = iq.send()
|
||||||
response = self.send(out, self.makeIqResult(id))
|
#response = self.send(iq, self.Iq(sid=iq['id']))
|
||||||
self.set_jid(response.find('{urn:ietf:params:xml:ns:xmpp-bind}bind/{urn:ietf:params:xml:ns:xmpp-bind}jid').text)
|
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)
|
logging.info("Node set to: %s" % self.fulljid)
|
||||||
if "{urn:ietf:params:xml:ns:xmpp-session}session" not in self.features:
|
if "{urn:ietf:params:xml:ns:xmpp-session}session" not in self.features:
|
||||||
logging.debug("Established Session")
|
logging.debug("Established Session")
|
||||||
|
@ -261,21 +242,11 @@ class ClientXMPP(basexmpp, XMLStream):
|
||||||
self.sessionstarted = True
|
self.sessionstarted = True
|
||||||
self.event("session_start")
|
self.event("session_start")
|
||||||
|
|
||||||
def _handleRoster(self, roster):
|
def _handleRoster(self, iq):
|
||||||
xml = roster.xml
|
for jid in iq['roster']['items']:
|
||||||
xml = roster.xml
|
if not jid.bare in self.roster:
|
||||||
roster_update = {}
|
self.roster[jid.bare] = {'groups': [], 'name': '', 'subscription': 'none', 'presence': {}, 'in_roster': True}
|
||||||
for item in xml.findall('{jabber:iq:roster}query/{jabber:iq:roster}item'):
|
self.roster[jid.bare].update(iq['roster']['jid'])
|
||||||
if not item.attrib['jid'] in self.roster:
|
if iq['type'] == 'set':
|
||||||
self.roster[item.attrib['jid']] = {'groups': [], 'name': '', 'subscription': 'none', 'presence': {}, 'in_roster': False}
|
self.send(self.Iq().setValues({'type': 'result', 'id': iq['id']}).enable('roster'))
|
||||||
self.roster[item.attrib['jid']]['name'] = item.get('name', '')
|
self.event("roster_update", iq)
|
||||||
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)
|
|
||||||
|
|
|
@ -28,61 +28,22 @@ from . xmlstream.handler.callback import Callback
|
||||||
from . import plugins
|
from . import plugins
|
||||||
from . stanza.message import Message
|
from . stanza.message import Message
|
||||||
from . stanza.iq import Iq
|
from . stanza.iq import Iq
|
||||||
|
from . stanza.presence import Presence
|
||||||
|
from . stanza.roster import Roster
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import threading
|
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):
|
class basexmpp(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.id = 0
|
self.id = 0
|
||||||
self.id_lock = threading.Lock()
|
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.sentpresence = False
|
||||||
self.fulljid = ''
|
self.fulljid = ''
|
||||||
self.resource = ''
|
self.resource = ''
|
||||||
|
@ -94,11 +55,21 @@ class basexmpp(object):
|
||||||
self.auto_subscribe = True
|
self.auto_subscribe = True
|
||||||
self.event_handlers = {}
|
self.event_handlers = {}
|
||||||
self.roster = {}
|
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('IM', MatchXMLMask("<message xmlns='%s'><body /></message>" % self.default_ns), self._handleMessage))
|
||||||
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('Presence', MatchXMLMask("<presence xmlns='%s' />" % self.default_ns), self._handlePresence))
|
||||||
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.add_event_handler('presence_subscribe', self._handlePresenceSubscribe)
|
||||||
self.registerStanza(Message)
|
self.registerStanza(Message)
|
||||||
self.registerStanza(Iq)
|
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):
|
def set_jid(self, jid):
|
||||||
"""Rip a JID apart and claim it as our own."""
|
"""Rip a JID apart and claim it as our own."""
|
||||||
|
@ -147,11 +118,17 @@ class basexmpp(object):
|
||||||
|
|
||||||
def getId(self):
|
def getId(self):
|
||||||
return "%x".upper() % self.id
|
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,))
|
#logging.warning("Deprecated send used for \"%s\"" % (data,))
|
||||||
if not type(data) == type(''):
|
#if not type(data) == type(''):
|
||||||
data = self.tostring(data)
|
# data = self.tostring(data)
|
||||||
|
if hasattr(mask, 'xml'):
|
||||||
|
mask = mask.xml
|
||||||
|
data = str(data)
|
||||||
if mask is not None:
|
if mask is not None:
|
||||||
waitfor = XMLWaiter('SendWait_%s' % self.getNewId(), MatchXMLMask(mask))
|
waitfor = XMLWaiter('SendWait_%s' % self.getNewId(), MatchXMLMask(mask))
|
||||||
self.registerHandler(waitfor)
|
self.registerHandler(waitfor)
|
||||||
|
@ -160,86 +137,28 @@ class basexmpp(object):
|
||||||
return waitfor.wait(timeout)
|
return waitfor.wait(timeout)
|
||||||
|
|
||||||
def makeIq(self, id=0, ifrom=None):
|
def makeIq(self, id=0, ifrom=None):
|
||||||
iq = ET.Element('{%s}iq' % self.default_ns)
|
return self.Iq().setValues({'id': id, 'from': ifrom})
|
||||||
if id == 0:
|
|
||||||
id = self.getNewId()
|
|
||||||
iq.set('id', str(id))
|
|
||||||
if ifrom is not None:
|
|
||||||
iq.attrib['from'] = ifrom
|
|
||||||
return iq
|
|
||||||
|
|
||||||
def makeIqGet(self, queryxmlns = None):
|
def makeIqGet(self, queryxmlns = None):
|
||||||
iq = self.makeIq()
|
iq = self.Iq().setValues({'type': 'get'})
|
||||||
iq.set('type', 'get')
|
|
||||||
if queryxmlns:
|
if queryxmlns:
|
||||||
query = ET.Element("{%s}query" % queryxmlns)
|
iq.append(ET.Element("{%s}query" % queryxmlns))
|
||||||
iq.append(query)
|
|
||||||
return iq
|
return iq
|
||||||
|
|
||||||
def makeIqResult(self, id):
|
def makeIqResult(self, id):
|
||||||
iq = self.makeIq(id)
|
return self.Iq().setValues({'id': id, 'type': 'result'})
|
||||||
iq.set('type', 'result')
|
|
||||||
return iq
|
|
||||||
|
|
||||||
def makeIqSet(self, sub=None):
|
def makeIqSet(self, sub=None):
|
||||||
iq = self.makeIq()
|
iq = self.Iq().setValues({'type': 'set'})
|
||||||
iq.set('type', 'set')
|
|
||||||
if sub != None:
|
if sub != None:
|
||||||
iq.append(sub)
|
iq.append(sub)
|
||||||
return iq
|
return iq
|
||||||
|
|
||||||
def makeIqError(self, id):
|
def makeIqError(self, id, type='cancel', condition='feature-not-implemented', text=None):
|
||||||
iq = self.makeIq(id)
|
iq = self.Iq().setValues({'id': id})
|
||||||
iq.set('type', 'error')
|
iq['error'].setValues({'type': type, 'condition': condition, 'text': text})
|
||||||
return iq
|
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):
|
def makeIqQuery(self, iq, xmlns):
|
||||||
query = ET.Element("{%s}query" % xmlns)
|
query = ET.Element("{%s}query" % xmlns)
|
||||||
iq.append(query)
|
iq.append(query)
|
||||||
|
@ -355,61 +274,21 @@ class basexmpp(object):
|
||||||
|
|
||||||
def _handleMessage(self, msg):
|
def _handleMessage(self, msg):
|
||||||
self.event('message', 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):
|
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"""
|
"""Update roster items based on presence"""
|
||||||
show = xml.find('%sshow' % ns)
|
self.event("presence_%s" % presence['type'], presence)
|
||||||
status = xml.find('%sstatus' % ns)
|
if not presence['type'] in ('available', 'unavailable'):
|
||||||
priority = xml.find('%spriority' % ns)
|
return
|
||||||
fulljid = xml.attrib['from']
|
jid = presence['from'].bare
|
||||||
to = xml.attrib['to']
|
resource = presence['from'].resource
|
||||||
resource = self.getjidresource(fulljid)
|
show = presence['type']
|
||||||
if not resource:
|
status = presence['status']
|
||||||
resouce = None
|
priority = presence['priority']
|
||||||
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)
|
|
||||||
wasoffline = False
|
wasoffline = False
|
||||||
oldroster = self.roster.get(jid, {}).get(resource, {})
|
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}
|
self.roster[jid] = {'groups': [], 'name': '', 'subscription': 'none', 'presence': {}, 'in_roster': False}
|
||||||
if not resource in self.roster[jid]['presence']:
|
if not resource in self.roster[jid]['presence']:
|
||||||
wasoffline = True
|
wasoffline = True
|
||||||
self.roster[jid]['presence'][resource] = {'show': show, 'status': status, 'priority': priority}
|
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':
|
if self.roster[jid]['presence'][resource].get('show', None) == 'unavailable':
|
||||||
wasoffline = True
|
wasoffline = True
|
||||||
self.roster[jid]['presence'][resource] = {'show': show, 'status': status}
|
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', '')
|
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', 'ffc'):
|
||||||
if wasoffline and show in ('available', 'away', 'xa', 'na'):
|
self.event("got_online", presence)
|
||||||
self.event("got_online", eventdata)
|
|
||||||
elif not wasoffline and show == 'unavailable':
|
elif not wasoffline and show == 'unavailable':
|
||||||
self.event("got_offline", eventdata)
|
self.event("got_offline", eventdata)
|
||||||
if len(self.roster[jid]['presence']) > 1:
|
if len(self.roster[jid]['presence']) > 1:
|
||||||
|
@ -430,7 +307,7 @@ class basexmpp(object):
|
||||||
else:
|
else:
|
||||||
del self.roster[jid]
|
del self.roster[jid]
|
||||||
elif oldroster != self.roster.get(jid, {'presence': {}})['presence'].get(resource, {}) and show != 'unavailable':
|
elif oldroster != self.roster.get(jid, {'presence': {}})['presence'].get(resource, {}) and show != 'unavailable':
|
||||||
self.event("changed_status", eventdata)
|
self.event("changed_status", presence)
|
||||||
name = ''
|
name = ''
|
||||||
if name:
|
if name:
|
||||||
name = "(%s) " % name
|
name = "(%s) " % name
|
||||||
|
@ -438,13 +315,9 @@ class basexmpp(object):
|
||||||
|
|
||||||
def _handlePresenceSubscribe(self, presence):
|
def _handlePresenceSubscribe(self, presence):
|
||||||
"""Handling subscriptions automatically."""
|
"""Handling subscriptions automatically."""
|
||||||
xml = presence.xml
|
|
||||||
if self.auto_authorize == True:
|
if self.auto_authorize == True:
|
||||||
#self.updateRoster(self.getjidbare(xml.attrib['from']))
|
self.send(self.makePresence(ptype='subscribed', pto=presence['from'].bare))
|
||||||
self.send(self.makePresence(ptype='subscribed', pto=self.getjidbare(xml.attrib['from'])))
|
|
||||||
if self.auto_subscribe:
|
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:
|
elif self.auto_authorize == False:
|
||||||
self.send(self.makePresence(ptype='unsubscribed', pto=self.getjidbare(xml.attrib['from'])))
|
self.send(self.makePresence(ptype='unsubscribed', pto=presence['from'].bare))
|
||||||
elif self.auto_authorize == None:
|
|
||||||
pass
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
from .. xmlstream.stanzabase import StanzaBase
|
from .. xmlstream.stanzabase import StanzaBase
|
||||||
from xml.etree import cElementTree as ET
|
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):
|
class Iq(StanzaBase):
|
||||||
interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject'))
|
interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject'))
|
||||||
|
@ -11,7 +14,6 @@ class Iq(StanzaBase):
|
||||||
StanzaBase.__init__(self, *args, **kwargs)
|
StanzaBase.__init__(self, *args, **kwargs)
|
||||||
if self['id'] == '':
|
if self['id'] == '':
|
||||||
self['id'] = self.stream.getId()
|
self['id'] = self.stream.getId()
|
||||||
print("________LOADED IQ CLASS")
|
|
||||||
|
|
||||||
def result(self):
|
def result(self):
|
||||||
self['type'] = 'result'
|
self['type'] = 'result'
|
||||||
|
@ -37,3 +39,19 @@ class Iq(StanzaBase):
|
||||||
def unhandled(self):
|
def unhandled(self):
|
||||||
pass
|
pass
|
||||||
# returned unhandled error
|
# 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 .. xmlstream.stanzabase import StanzaBase
|
||||||
from xml.etree import cElementTree as ET
|
from xml.etree import cElementTree as ET
|
||||||
|
from . error import Error
|
||||||
|
|
||||||
class Message(StanzaBase):
|
class Message(StanzaBase):
|
||||||
interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject'))
|
interfaces = set(('type', 'to', 'from', 'id', 'body', 'subject'))
|
||||||
|
@ -25,21 +26,5 @@ class Message(StanzaBase):
|
||||||
self['body'] = body
|
self['body'] = body
|
||||||
return self
|
return self
|
||||||
|
|
||||||
if __name__ == '__main__':
|
Message.plugin_attrib_map['error'] = Error
|
||||||
m = Message()
|
Message.plugin_tag_map["{%s}%s" % (Error.namespace, Error.name)] = Error
|
||||||
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'])
|
|
||||||
|
|
|
@ -1,21 +1,51 @@
|
||||||
from .. xmlstream.stanzabase import StanzaBase
|
from .. xmlstream.stanzabase import StanzaBase
|
||||||
from .. xmlstream import xmlstream as xmlstreammod
|
from xml.etree import cElementTree as ET
|
||||||
from .. xmlstream.matcher.xpath import MatchXPath
|
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):
|
def getShowElement(self):
|
||||||
class PresenceStanza(StanzaBase):
|
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):
|
def getPriority(self):
|
||||||
self.pfrom = ''
|
p = self._getSubText('priority')
|
||||||
self.pto = ''
|
if not p: p = 0
|
||||||
StanzaBase.__init__(self, stream, xml, xmlstreammod.stanza_extensions.get('PresenceStanza', []))
|
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):
|
Presence.plugin_attrib_map['error'] = Error
|
||||||
StanzaBase.fromXML(self, xml)
|
Presence.plugin_tag_map["{%s}%s" % (Error.namespace, Error.name)] = Error
|
||||||
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},)
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from . import base
|
from . import base
|
||||||
import queue
|
import queue
|
||||||
import logging
|
import logging
|
||||||
|
from .. stanzabase import StanzaBase
|
||||||
|
|
||||||
class Waiter(base.BaseHandler):
|
class Waiter(base.BaseHandler):
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ class Waiter(base.BaseHandler):
|
||||||
return self._payload.get(True, timeout)
|
return self._payload.get(True, timeout)
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
logging.warning("Timed out waiting for %s" % self.name)
|
logging.warning("Timed out waiting for %s" % self.name)
|
||||||
return False
|
return StanzaBase(stype='error')
|
||||||
|
|
||||||
def checkDelete(self):
|
def checkDelete(self):
|
||||||
return True
|
return True
|
||||||
|
|
|
@ -13,33 +13,53 @@ class JID(object):
|
||||||
return self.jid.split('@', 1)[-1].split('/', 1)[0]
|
return self.jid.split('@', 1)[-1].split('/', 1)[0]
|
||||||
elif name == 'full':
|
elif name == 'full':
|
||||||
return self.jid
|
return self.jid
|
||||||
|
elif name == 'bare':
|
||||||
|
return self.jid.split('/', 1)[0]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.jid
|
return self.jid
|
||||||
|
|
||||||
class StanzaBase(object):
|
class ElementBase(object):
|
||||||
name = 'stanza'
|
name = 'stanza'
|
||||||
|
plugin_attrib = 'plugin'
|
||||||
namespace = 'jabber:client'
|
namespace = 'jabber:client'
|
||||||
interfaces = set(('type', 'to', 'from', 'id', 'payload'))
|
interfaces = set(('type', 'to', 'from', 'id', 'payload'))
|
||||||
types = set(('get', 'set', 'error', None, 'unavailable', 'normal', 'chat'))
|
types = set(('get', 'set', 'error', None, 'unavailable', 'normal', 'chat'))
|
||||||
sub_interfaces = tuple()
|
sub_interfaces = tuple()
|
||||||
|
plugin_attrib_map = {}
|
||||||
|
plugin_tag_map = {}
|
||||||
|
|
||||||
def __init__(self, stream, xml=None, stype=None, sto=None, sfrom=None, sid=None):
|
def __init__(self, xml=None, parent=None):
|
||||||
self.stream = stream
|
self.parent = parent
|
||||||
self.xml = xml
|
self.xml = xml
|
||||||
if xml is None:
|
self.plugins = {}
|
||||||
self.xml = ET.Element("{%(namespace)s}%(name)s" % {'name': self.name, 'namespace': self.namespace})
|
if not self.setup(xml) and len(self.plugin_attrib_map):
|
||||||
if stype is not None:
|
for child in self.xml.getchildren():
|
||||||
self['type'] = stype
|
if child.tag in self.plugin_tag_map:
|
||||||
if sto is not None:
|
self.plugins[self.plugin_tag_map[child.tag].plugin_attrib] = self.plugin_tag_map[child.tag](xml=child, parent=self)
|
||||||
self['to'] = sto
|
|
||||||
if sfrom is not None:
|
|
||||||
self['from'] = sfrom
|
|
||||||
self.tag = "{%s}%s" % (self.stream.default_ns, self.name)
|
|
||||||
|
|
||||||
def match(self, xml):
|
def match(self, xml):
|
||||||
return xml.tag == self.tag
|
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):
|
def __getitem__(self, attrib):
|
||||||
if attrib in self.interfaces:
|
if attrib in self.interfaces:
|
||||||
if hasattr(self, "get%s" % attrib.title()):
|
if hasattr(self, "get%s" % attrib.title()):
|
||||||
|
@ -49,11 +69,14 @@ class StanzaBase(object):
|
||||||
return self._getSubText(attrib)
|
return self._getSubText(attrib)
|
||||||
else:
|
else:
|
||||||
return self._getAttr(attrib)
|
return self._getAttr(attrib)
|
||||||
|
elif attrib in self.plugin_attrib_map:
|
||||||
|
self.initPlugin(attrib)
|
||||||
|
return self.plugins[attrib]
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def __setitem__(self, attrib, value):
|
def __setitem__(self, attrib, value):
|
||||||
if attrib.lower() in self.interfaces:
|
if attrib in self.interfaces:
|
||||||
if value is not None:
|
if value is not None:
|
||||||
if hasattr(self, "set%s" % attrib.title()):
|
if hasattr(self, "set%s" % attrib.title()):
|
||||||
getattr(self, "set%s" % attrib.title())(value,)
|
getattr(self, "set%s" % attrib.title())(value,)
|
||||||
|
@ -64,6 +87,9 @@ class StanzaBase(object):
|
||||||
self._setAttr(attrib, value)
|
self._setAttr(attrib, value)
|
||||||
else:
|
else:
|
||||||
self.__delitem__(attrib)
|
self.__delitem__(attrib)
|
||||||
|
elif attrib in self.plugin_map:
|
||||||
|
self.initPlugin(attrib)
|
||||||
|
self.plugins[attrib].setValues(value)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __delitem__(self, attrib):
|
def __delitem__(self, attrib):
|
||||||
|
@ -75,62 +101,18 @@ class StanzaBase(object):
|
||||||
return self._delSub(attrib)
|
return self._delSub(attrib)
|
||||||
else:
|
else:
|
||||||
self._delAttr(attrib)
|
self._delAttr(attrib)
|
||||||
return self
|
elif attrib in self.plugin_map:
|
||||||
|
if attrib in self.plugins:
|
||||||
def setType(self, value):
|
del self.plugins[attrib]
|
||||||
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
|
return self
|
||||||
|
|
||||||
def error(self):
|
def __eq__(self, other):
|
||||||
self['type'] = 'error'
|
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):
|
def _setAttr(self, name, value):
|
||||||
self.xml.attrib[name] = value
|
self.xml.attrib[name] = value
|
||||||
|
|
||||||
|
@ -162,6 +144,87 @@ class StanzaBase(object):
|
||||||
if child.tag == "{%s}%s" % (self.namespace, name):
|
if child.tag == "{%s}%s" % (self.namespace, name):
|
||||||
self.xml.remove(child)
|
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):
|
def unhandled(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -226,15 +289,3 @@ class StanzaBase(object):
|
||||||
text[cc] = '"'
|
text[cc] = '"'
|
||||||
cc += 1
|
cc += 1
|
||||||
return ''.join(text)
|
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