Merge branch 'roster' of github.com:fritzy/SleekXMPP into roster

Conflicts:
	sleekxmpp/basexmpp.py
	sleekxmpp/roster.py
	sleekxmpp/test/sleektest.py
	tests/test_stream_presence.py
	tests/test_stream_roster.py
This commit is contained in:
Lance Stout 2010-11-17 02:00:20 -05:00
commit 26aca2b789
47 changed files with 920 additions and 938 deletions

BIN
sleekxmpp/__init__$py.class Normal file

Binary file not shown.

View file

@ -27,13 +27,7 @@ from sleekxmpp.xmlstream.matcher import *
from sleekxmpp.xmlstream.handler import * from sleekxmpp.xmlstream.handler import *
# Flag indicating if DNS SRV records are available for use. log = logging.getLogger(__name__)
SRV_SUPPORT = True
try:
import dns.resolver
except:
SRV_SUPPORT = False
# In order to make sure that Unicode is handled properly # In order to make sure that Unicode is handled properly
# in Python 2.x, reset the default encoding. # in Python 2.x, reset the default encoding.
@ -205,9 +199,9 @@ class BaseXMPP(XMLStream):
xep = "(XEP-%s) " % self.plugin[plugin].xep xep = "(XEP-%s) " % self.plugin[plugin].xep
desc = (xep, self.plugin[plugin].description) desc = (xep, self.plugin[plugin].description)
logging.debug("Loaded Plugin %s%s" % desc) log.debug("Loaded Plugin %s%s" % desc)
except: except:
logging.exception("Unable to load plugin: %s", plugin) log.exception("Unable to load plugin: %s", plugin)
def register_plugins(self): def register_plugins(self):
""" """
@ -241,7 +235,7 @@ class BaseXMPP(XMLStream):
if key in self.plugin: if key in self.plugin:
return self.plugin[key] return self.plugin[key]
else: else:
logging.warning("""Plugin "%s" is not loaded.""" % key) log.warning("""Plugin "%s" is not loaded.""" % key)
return False return False
def get(self, key, default): def get(self, key, default):
@ -459,12 +453,12 @@ class BaseXMPP(XMLStream):
""" """
Attribute accessor for bare jid Attribute accessor for bare jid
""" """
logging.warning("jid property deprecated. Use boundjid.bare") log.warning("jid property deprecated. Use boundjid.bare")
return self.boundjid.bare return self.boundjid.bare
@jid.setter @jid.setter
def jid(self, value): def jid(self, value):
logging.warning("jid property deprecated. Use boundjid.bare") log.warning("jid property deprecated. Use boundjid.bare")
self.boundjid.bare = value self.boundjid.bare = value
@property @property
@ -472,12 +466,12 @@ class BaseXMPP(XMLStream):
""" """
Attribute accessor for full jid Attribute accessor for full jid
""" """
logging.warning("fulljid property deprecated. Use boundjid.full") log.warning("fulljid property deprecated. Use boundjid.full")
return self.boundjid.full return self.boundjid.full
@fulljid.setter @fulljid.setter
def fulljid(self, value): def fulljid(self, value):
logging.warning("fulljid property deprecated. Use boundjid.full") log.warning("fulljid property deprecated. Use boundjid.full")
self.boundjid.full = value self.boundjid.full = value
@property @property
@ -485,12 +479,12 @@ class BaseXMPP(XMLStream):
""" """
Attribute accessor for jid resource Attribute accessor for jid resource
""" """
logging.warning("resource property deprecated. Use boundjid.resource") log.warning("resource property deprecated. Use boundjid.resource")
return self.boundjid.resource return self.boundjid.resource
@resource.setter @resource.setter
def resource(self, value): def resource(self, value):
logging.warning("fulljid property deprecated. Use boundjid.full") log.warning("fulljid property deprecated. Use boundjid.full")
self.boundjid.resource = value self.boundjid.resource = value
@property @property
@ -498,12 +492,12 @@ class BaseXMPP(XMLStream):
""" """
Attribute accessor for jid usernode Attribute accessor for jid usernode
""" """
logging.warning("username property deprecated. Use boundjid.user") log.warning("username property deprecated. Use boundjid.user")
return self.boundjid.user return self.boundjid.user
@username.setter @username.setter
def username(self, value): def username(self, value):
logging.warning("username property deprecated. Use boundjid.user") log.warning("username property deprecated. Use boundjid.user")
self.boundjid.user = value self.boundjid.user = value
@property @property
@ -511,17 +505,17 @@ class BaseXMPP(XMLStream):
""" """
Attribute accessor for jid host Attribute accessor for jid host
""" """
logging.warning("server property deprecated. Use boundjid.host") log.warning("server property deprecated. Use boundjid.host")
return self.boundjid.server return self.boundjid.server
@server.setter @server.setter
def server(self, value): def server(self, value):
logging.warning("server property deprecated. Use boundjid.host") log.warning("server property deprecated. Use boundjid.host")
self.boundjid.server = value self.boundjid.server = value
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."""
logging.debug("setting jid to %s" % jid) log.debug("setting jid to %s" % jid)
self.boundjid.full = jid self.boundjid.full = jid
def getjidresource(self, fulljid): def getjidresource(self, fulljid):
@ -596,6 +590,5 @@ class BaseXMPP(XMLStream):
return return
self.event("changed_status", presence) self.event("changed_status", presence)
# Restore the old, lowercased name for backwards compatibility. # Restore the old, lowercased name for backwards compatibility.
basexmpp = BaseXMPP basexmpp = BaseXMPP

View file

@ -32,6 +32,9 @@ except:
SRV_SUPPORT = False SRV_SUPPORT = False
log = logging.getLogger(__name__)
class ClientXMPP(BaseXMPP): class ClientXMPP(BaseXMPP):
""" """
@ -132,7 +135,7 @@ class ClientXMPP(BaseXMPP):
def _session_timeout_check(self): def _session_timeout_check(self):
if not self.session_started_event.isSet(): if not self.session_started_event.isSet():
logging.debug("Session start has taken more than 15 seconds") log.debug("Session start has taken more than 15 seconds")
self.disconnect(reconnect=self.auto_reconnect) self.disconnect(reconnect=self.auto_reconnect)
def connect(self, address=tuple()): def connect(self, address=tuple()):
@ -149,19 +152,19 @@ class ClientXMPP(BaseXMPP):
self.session_started_event.clear() self.session_started_event.clear()
if not address or len(address) < 2: if not address or len(address) < 2:
if not self.srv_support: if not self.srv_support:
logging.debug("Did not supply (address, port) to connect" + \ log.debug("Did not supply (address, port) to connect" + \
" to and no SRV support is installed" + \ " to and no SRV support is installed" + \
" (http://www.dnspython.org)." + \ " (http://www.dnspython.org)." + \
" Continuing to attempt connection, using" + \ " Continuing to attempt connection, using" + \
" server hostname from JID.") " server hostname from JID.")
else: else:
logging.debug("Since no address is supplied," + \ log.debug("Since no address is supplied," + \
"attempting SRV lookup.") "attempting SRV lookup.")
try: try:
xmpp_srv = "_xmpp-client._tcp.%s" % self.server xmpp_srv = "_xmpp-client._tcp.%s" % self.server
answers = dns.resolver.query(xmpp_srv, dns.rdatatype.SRV) answers = dns.resolver.query(xmpp_srv, dns.rdatatype.SRV)
except dns.resolver.NXDOMAIN: except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
logging.debug("No appropriate SRV record found." + \ log.debug("No appropriate SRV record found." + \
" Using JID server name.") " Using JID server name.")
else: else:
# Pick a random server, weighted by priority. # Pick a random server, weighted by priority.
@ -275,7 +278,7 @@ class ClientXMPP(BaseXMPP):
self.send_xml(xml) self.send_xml(xml)
return True return True
else: else:
logging.warning("The module tlslite is required to log in" +\ log.warning("The module tlslite is required to log in" +\
" to some servers, and has not been found.") " to some servers, and has not been found.")
return False return False
@ -285,7 +288,7 @@ class ClientXMPP(BaseXMPP):
Restarts the stream. Restarts the stream.
""" """
logging.debug("Starting TLS") log.debug("Starting TLS")
if self.start_tls(): if self.start_tls():
raise RestartStream() raise RestartStream()
@ -299,7 +302,7 @@ class ClientXMPP(BaseXMPP):
if '{urn:ietf:params:xml:ns:xmpp-tls}starttls' in self.features: if '{urn:ietf:params:xml:ns:xmpp-tls}starttls' in self.features:
return False return False
logging.debug("Starting SASL Auth") log.debug("Starting SASL Auth")
sasl_ns = 'urn:ietf:params:xml:ns:xmpp-sasl' sasl_ns = 'urn:ietf:params:xml:ns:xmpp-sasl'
self.add_handler("<success xmlns='%s' />" % sasl_ns, self.add_handler("<success xmlns='%s' />" % sasl_ns,
self._handle_auth_success, self._handle_auth_success,
@ -333,7 +336,7 @@ class ClientXMPP(BaseXMPP):
sasl_ns, sasl_ns,
'ANONYMOUS')) 'ANONYMOUS'))
else: else:
logging.error("No appropriate login method.") log.error("No appropriate login method.")
self.disconnect() self.disconnect()
return True return True
@ -355,7 +358,7 @@ class ClientXMPP(BaseXMPP):
Arguments: Arguments:
xml -- The SASL authentication failure element. xml -- The SASL authentication failure element.
""" """
logging.info("Authentication failed.") log.info("Authentication failed.")
self.event("failed_auth", direct=True) self.event("failed_auth", direct=True)
self.disconnect() self.disconnect()
@ -366,7 +369,7 @@ class ClientXMPP(BaseXMPP):
Arguments: Arguments:
xml -- The bind feature element. xml -- The bind feature element.
""" """
logging.debug("Requesting resource: %s" % self.boundjid.resource) log.debug("Requesting resource: %s" % self.boundjid.resource)
xml.clear() xml.clear()
iq = self.Iq(stype='set') iq = self.Iq(stype='set')
if self.boundjid.resource: if self.boundjid.resource:
@ -380,10 +383,10 @@ class ClientXMPP(BaseXMPP):
self.set_jid(response.xml.find('{%s}bind/{%s}jid' % (bind_ns, self.set_jid(response.xml.find('{%s}bind/{%s}jid' % (bind_ns,
bind_ns)).text) bind_ns)).text)
self.bound = True self.bound = True
logging.info("Node set to: %s" % self.boundjid.fulljid) log.info("Node set to: %s" % self.boundjid.fulljid)
session_ns = 'urn:ietf:params:xml:ns:xmpp-session' session_ns = 'urn:ietf:params:xml:ns:xmpp-session'
if "{%s}session" % session_ns not in self.features or self.bindfail: if "{%s}session" % session_ns not in self.features or self.bindfail:
logging.debug("Established Session") log.debug("Established Session")
self.sessionstarted = True self.sessionstarted = True
self.session_started_event.set() self.session_started_event.set()
self.event("session_start") self.event("session_start")
@ -398,7 +401,7 @@ class ClientXMPP(BaseXMPP):
if self.authenticated and self.bound: if self.authenticated and self.bound:
iq = self.makeIqSet(xml) iq = self.makeIqSet(xml)
response = iq.send() response = iq.send()
logging.debug("Established Session") log.debug("Established Session")
self.sessionstarted = True self.sessionstarted = True
self.session_started_event.set() self.session_started_event.set()
self.event("session_start") self.event("session_start")

View file

@ -15,13 +15,16 @@ import hashlib
from sleekxmpp import plugins from sleekxmpp import plugins
from sleekxmpp import stanza from sleekxmpp import stanza
from sleekxmpp.basexmpp import BaseXMPP, SRV_SUPPORT from sleekxmpp.basexmpp import BaseXMPP
from sleekxmpp.xmlstream import XMLStream, RestartStream from sleekxmpp.xmlstream import XMLStream, RestartStream
from sleekxmpp.xmlstream import StanzaBase, ET from sleekxmpp.xmlstream import StanzaBase, ET
from sleekxmpp.xmlstream.matcher import * from sleekxmpp.xmlstream.matcher import *
from sleekxmpp.xmlstream.handler import * from sleekxmpp.xmlstream.handler import *
log = logging.getLogger(__name__)
class ComponentXMPP(BaseXMPP): class ComponentXMPP(BaseXMPP):
""" """
@ -82,7 +85,7 @@ class ComponentXMPP(BaseXMPP):
Overrides XMLStream.connect. Overrides XMLStream.connect.
""" """
logging.debug("Connecting to %s:%s" % (self.server_host, log.debug("Connecting to %s:%s" % (self.server_host,
self.server_port)) self.server_port))
return XMLStream.connect(self, self.server_host, return XMLStream.connect(self, self.server_host,
self.server_port) self.server_port)

View file

@ -6,5 +6,5 @@
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
__all__ = ['xep_0004', 'xep_0012', 'xep_0030', 'xep_0033', 'xep_0045', __all__ = ['xep_0004', 'xep_0012', 'xep_0030', 'xep_0033', 'xep_0045',
'xep_0050', 'xep_0078', 'xep_0085', 'xep_0092', 'xep_0199', 'xep_0050', 'xep_0085', 'xep_0092', 'xep_0199', 'gmail_notify',
'gmail_notify', 'xep_0060', 'xep_0202'] 'xep_0060', 'xep_0202']

View file

@ -14,6 +14,9 @@ from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID
from .. stanza.iq import Iq from .. stanza.iq import Iq
log = logging.getLogger(__name__)
class GmailQuery(ElementBase): class GmailQuery(ElementBase):
namespace = 'google:mail:notify' namespace = 'google:mail:notify'
name = 'query' name = 'query'
@ -34,12 +37,12 @@ class MailBox(ElementBase):
namespace = 'google:mail:notify' namespace = 'google:mail:notify'
name = 'mailbox' name = 'mailbox'
plugin_attrib = 'mailbox' plugin_attrib = 'mailbox'
interfaces = set(('result-time', 'total-matched', 'total-estimate', interfaces = set(('result-time', 'total-matched', 'total-estimate',
'url', 'threads', 'matched', 'estimate')) 'url', 'threads', 'matched', 'estimate'))
def getThreads(self): def getThreads(self):
threads = [] threads = []
for threadXML in self.xml.findall('{%s}%s' % (MailThread.namespace, for threadXML in self.xml.findall('{%s}%s' % (MailThread.namespace,
MailThread.name)): MailThread.name)):
threads.append(MailThread(xml=threadXML, parent=None)) threads.append(MailThread(xml=threadXML, parent=None))
return threads return threads
@ -55,10 +58,10 @@ class MailThread(ElementBase):
namespace = 'google:mail:notify' namespace = 'google:mail:notify'
name = 'mail-thread-info' name = 'mail-thread-info'
plugin_attrib = 'thread' plugin_attrib = 'thread'
interfaces = set(('tid', 'participation', 'messages', 'date', interfaces = set(('tid', 'participation', 'messages', 'date',
'senders', 'url', 'labels', 'subject', 'snippet')) 'senders', 'url', 'labels', 'subject', 'snippet'))
sub_interfaces = set(('labels', 'subject', 'snippet')) sub_interfaces = set(('labels', 'subject', 'snippet'))
def getSenders(self): def getSenders(self):
senders = [] senders = []
sendersXML = self.xml.find('{%s}senders' % self.namespace) sendersXML = self.xml.find('{%s}senders' % self.namespace)
@ -91,13 +94,13 @@ class gmail_notify(base.base_plugin):
""" """
Google Talk: Gmail Notifications Google Talk: Gmail Notifications
""" """
def plugin_init(self): def plugin_init(self):
self.description = 'Google Talk: Gmail Notifications' self.description = 'Google Talk: Gmail Notifications'
self.xmpp.registerHandler( self.xmpp.registerHandler(
Callback('Gmail Result', Callback('Gmail Result',
MatchXPath('{%s}iq/{%s}%s' % (self.xmpp.default_ns, MatchXPath('{%s}iq/{%s}%s' % (self.xmpp.default_ns,
MailBox.namespace, MailBox.namespace,
MailBox.name)), MailBox.name)),
self.handle_gmail)) self.handle_gmail))
@ -108,7 +111,7 @@ class gmail_notify(base.base_plugin):
NewMail.namespace, NewMail.namespace,
NewMail.name)), NewMail.name)),
self.handle_new_mail)) self.handle_new_mail))
registerStanzaPlugin(Iq, GmailQuery) registerStanzaPlugin(Iq, GmailQuery)
registerStanzaPlugin(Iq, MailBox) registerStanzaPlugin(Iq, MailBox)
registerStanzaPlugin(Iq, NewMail) registerStanzaPlugin(Iq, NewMail)
@ -118,12 +121,12 @@ class gmail_notify(base.base_plugin):
def handle_gmail(self, iq): def handle_gmail(self, iq):
mailbox = iq['mailbox'] mailbox = iq['mailbox']
approx = ' approximately' if mailbox['estimated'] else '' approx = ' approximately' if mailbox['estimated'] else ''
logging.info('Gmail: Received%s %s emails' % (approx, mailbox['total-matched'])) log.info('Gmail: Received%s %s emails' % (approx, mailbox['total-matched']))
self.last_result_time = mailbox['result-time'] self.last_result_time = mailbox['result-time']
self.xmpp.event('gmail_messages', iq) self.xmpp.event('gmail_messages', iq)
def handle_new_mail(self, iq): def handle_new_mail(self, iq):
logging.info("Gmail: New emails received!") log.info("Gmail: New emails received!")
self.xmpp.event('gmail_notify') self.xmpp.event('gmail_notify')
self.checkEmail() self.checkEmail()
@ -135,9 +138,9 @@ class gmail_notify(base.base_plugin):
def search(self, query=None, newer=None): def search(self, query=None, newer=None):
if query is None: if query is None:
logging.info("Gmail: Checking for new emails") log.info("Gmail: Checking for new emails")
else: else:
logging.info('Gmail: Searching for emails matching: "%s"' % query) log.info('Gmail: Searching for emails matching: "%s"' % query)
iq = self.xmpp.Iq() iq = self.xmpp.Iq()
iq['type'] = 'get' iq['type'] = 'get'
iq['to'] = self.xmpp.jid iq['to'] = self.xmpp.jid

View file

@ -3,15 +3,19 @@ import logging
from xml.etree import cElementTree as ET from xml.etree import cElementTree as ET
import types import types
log = logging.getLogger(__name__)
class jobs(base.base_plugin): class jobs(base.base_plugin):
def plugin_init(self): def plugin_init(self):
self.xep = 'pubsubjob' self.xep = 'pubsubjob'
self.description = "Job distribution over Pubsub" self.description = "Job distribution over Pubsub"
def post_init(self): def post_init(self):
pass pass
#TODO add event #TODO add event
def createJobNode(self, host, jid, node, config=None): def createJobNode(self, host, jid, node, config=None):
pass pass
@ -40,7 +44,7 @@ class jobs(base.base_plugin):
iq['psstate']['payload'] = state iq['psstate']['payload'] = state
result = iq.send() result = iq.send()
if result is None or type(result) == types.BooleanType or result['type'] != 'result': if result is None or type(result) == types.BooleanType or result['type'] != 'result':
logging.error("Unable to change %s:%s to %s" % (node, jobid, state)) log.error("Unable to change %s:%s to %s" % (node, jobid, state))
return False return False
return True return True

View file

@ -2,42 +2,46 @@
SleekXMPP: The Sleek XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
from . import base from . import base
import logging import log
from xml.etree import cElementTree as ET from xml.etree import cElementTree as ET
import copy import copy
import logging import logging
#TODO support item groups and results #TODO support item groups and results
log = logging.getLogger(__name__)
class old_0004(base.base_plugin): class old_0004(base.base_plugin):
def plugin_init(self): def plugin_init(self):
self.xep = '0004' self.xep = '0004'
self.description = '*Deprecated Data Forms' self.description = '*Deprecated Data Forms'
self.xmpp.add_handler("<message><x xmlns='jabber:x:data' /></message>", self.handler_message_xform, name='Old Message Form') self.xmpp.add_handler("<message><x xmlns='jabber:x:data' /></message>", self.handler_message_xform, name='Old Message Form')
def post_init(self): def post_init(self):
base.base_plugin.post_init(self) base.base_plugin.post_init(self)
self.xmpp.plugin['xep_0030'].add_feature('jabber:x:data') self.xmpp.plugin['xep_0030'].add_feature('jabber:x:data')
logging.warning("This implementation of XEP-0004 is deprecated.") log.warning("This implementation of XEP-0004 is deprecated.")
def handler_message_xform(self, xml): def handler_message_xform(self, xml):
object = self.handle_form(xml) object = self.handle_form(xml)
self.xmpp.event("message_form", object) self.xmpp.event("message_form", object)
def handler_presence_xform(self, xml): def handler_presence_xform(self, xml):
object = self.handle_form(xml) object = self.handle_form(xml)
self.xmpp.event("presence_form", object) self.xmpp.event("presence_form", object)
def handle_form(self, xml): def handle_form(self, xml):
xmlform = xml.find('{jabber:x:data}x') xmlform = xml.find('{jabber:x:data}x')
object = self.buildForm(xmlform) object = self.buildForm(xmlform)
self.xmpp.event("message_xform", object) self.xmpp.event("message_xform", object)
return object return object
def buildForm(self, xml): def buildForm(self, xml):
form = Form(ftype=xml.attrib['type']) form = Form(ftype=xml.attrib['type'])
form.fromXML(xml) form.fromXML(xml)
@ -51,12 +55,12 @@ class FieldContainer(object):
self.fields = [] self.fields = []
self.field = {} self.field = {}
self.stanza = stanza self.stanza = stanza
def addField(self, var, ftype='text-single', label='', desc='', required=False, value=None): def addField(self, var, ftype='text-single', label='', desc='', required=False, value=None):
self.field[var] = FormField(var, ftype, label, desc, required, value) self.field[var] = FormField(var, ftype, label, desc, required, value)
self.fields.append(self.field[var]) self.fields.append(self.field[var])
return self.field[var] return self.field[var]
def buildField(self, xml): def buildField(self, xml):
self.field[xml.get('var', '__unnamed__')] = FormField(xml.get('var', '__unnamed__'), xml.get('type', 'text-single')) self.field[xml.get('var', '__unnamed__')] = FormField(xml.get('var', '__unnamed__'), xml.get('type', 'text-single'))
self.fields.append(self.field[xml.get('var', '__unnamed__')]) self.fields.append(self.field[xml.get('var', '__unnamed__')])
@ -66,13 +70,13 @@ class FieldContainer(object):
self.stanza = xml.tag self.stanza = xml.tag
for field in xml.findall('{jabber:x:data}field'): for field in xml.findall('{jabber:x:data}field'):
self.buildField(field) self.buildField(field)
def getXML(self, ftype): def getXML(self, ftype):
container = ET.Element(self.stanza) container = ET.Element(self.stanza)
for field in self.fields: for field in self.fields:
container.append(field.getXML(ftype)) container.append(field.getXML(ftype))
return container return container
class Form(FieldContainer): class Form(FieldContainer):
types = ('form', 'submit', 'cancel', 'result') types = ('form', 'submit', 'cancel', 'result')
def __init__(self, xmpp=None, ftype='form', title='', instructions=''): def __init__(self, xmpp=None, ftype='form', title='', instructions=''):
@ -85,7 +89,7 @@ class Form(FieldContainer):
self.instructions = instructions self.instructions = instructions
self.reported = [] self.reported = []
self.items = [] self.items = []
def merge(self, form2): def merge(self, form2):
form1 = Form(ftype=self.type) form1 = Form(ftype=self.type)
form1.fromXML(self.getXML(self.type)) form1.fromXML(self.getXML(self.type))
@ -98,18 +102,18 @@ class Form(FieldContainer):
if (option, label) not in form1.field[field.var].options: if (option, label) not in form1.field[field.var].options:
form1.fields[field.var].addOption(option, label) form1.fields[field.var].addOption(option, label)
return form1 return form1
def copy(self): def copy(self):
newform = Form(ftype=self.type) newform = Form(ftype=self.type)
newform.fromXML(self.getXML(self.type)) newform.fromXML(self.getXML(self.type))
return newform return newform
def update(self, form): def update(self, form):
values = form.getValues() values = form.getValues()
for var in values: for var in values:
if var in self.fields: if var in self.fields:
self.fields[var].setValue(self.fields[var]) self.fields[var].setValue(self.fields[var])
def getValues(self): def getValues(self):
result = {} result = {}
for field in self.fields: for field in self.fields:
@ -118,7 +122,7 @@ class Form(FieldContainer):
value = value[0] value = value[0]
result[field.var] = value result[field.var] = value
return result return result
def setValues(self, values={}): def setValues(self, values={}):
for field in values: for field in values:
if field in self.field: if field in self.field:
@ -127,10 +131,10 @@ class Form(FieldContainer):
self.field[field].setValue(value) self.field[field].setValue(value)
else: else:
self.field[field].setValue(values[field]) self.field[field].setValue(values[field])
def fromXML(self, xml): def fromXML(self, xml):
self.buildForm(xml) self.buildForm(xml)
def addItem(self): def addItem(self):
newitem = FieldContainer('item') newitem = FieldContainer('item')
self.items.append(newitem) self.items.append(newitem)
@ -148,21 +152,21 @@ class Form(FieldContainer):
def buildReported(self, xml): def buildReported(self, xml):
reported = self.addReported() reported = self.addReported()
reported.buildContainer(xml) reported.buildContainer(xml)
def setTitle(self, title): def setTitle(self, title):
self.title = title self.title = title
def setInstructions(self, instructions): def setInstructions(self, instructions):
self.instructions = instructions self.instructions = instructions
def setType(self, ftype): def setType(self, ftype):
self.type = ftype self.type = ftype
def getXMLMessage(self, to): def getXMLMessage(self, to):
msg = self.xmpp.makeMessage(to) msg = self.xmpp.makeMessage(to)
msg.append(self.getXML()) msg.append(self.getXML())
return msg return msg
def buildForm(self, xml): def buildForm(self, xml):
self.type = xml.get('type', 'form') self.type = xml.get('type', 'form')
if xml.find('{jabber:x:data}title') is not None: if xml.find('{jabber:x:data}title') is not None:
@ -175,7 +179,7 @@ class Form(FieldContainer):
self.buildReported(reported) self.buildReported(reported)
for item in xml.findall('{jabber:x:data}item'): for item in xml.findall('{jabber:x:data}item'):
self.buildItem(item) self.buildItem(item)
#def getXML(self, tostring = False): #def getXML(self, tostring = False):
def getXML(self, ftype=None): def getXML(self, ftype=None):
if ftype: if ftype:
@ -199,7 +203,7 @@ class Form(FieldContainer):
#if tostring: #if tostring:
# form = self.xmpp.tostring(form) # form = self.xmpp.tostring(form)
return form return form
def getXHTML(self): def getXHTML(self):
form = ET.Element('{http://www.w3.org/1999/xhtml}form') form = ET.Element('{http://www.w3.org/1999/xhtml}form')
if self.title: if self.title:
@ -217,8 +221,8 @@ class Form(FieldContainer):
for field in self.items: for field in self.items:
form.append(field.getXHTML()) form.append(field.getXHTML())
return form return form
def makeSubmit(self): def makeSubmit(self):
self.setType('submit') self.setType('submit')
@ -246,13 +250,13 @@ class FormField(object):
self.islinebreak = False self.islinebreak = False
if value: if value:
self.setValue(value) self.setValue(value)
def addOption(self, value, label): def addOption(self, value, label):
if self.islist: if self.islist:
self.options.append((value, label)) self.options.append((value, label))
else: else:
raise ValueError("Cannot add options to non-list type field.") raise ValueError("Cannot add options to non-list type field.")
def setTrue(self): def setTrue(self):
if self.type == 'boolean': if self.type == 'boolean':
self.value = [True] self.value = [True]
@ -263,10 +267,10 @@ class FormField(object):
def require(self): def require(self):
self.required = True self.required = True
def setDescription(self, desc): def setDescription(self, desc):
self.desc = desc self.desc = desc
def setValue(self, value): def setValue(self, value):
if self.type == 'boolean': if self.type == 'boolean':
if value in ('1', 1, True, 'true', 'True', 'yes'): if value in ('1', 1, True, 'true', 'True', 'yes'):
@ -291,10 +295,10 @@ class FormField(object):
pass pass
else: else:
self.value = '' self.value = ''
def setAnswer(self, value): def setAnswer(self, value):
self.setValue(value) self.setValue(value)
def buildField(self, xml): def buildField(self, xml):
self.type = xml.get('type', 'text-single') self.type = xml.get('type', 'text-single')
self.label = xml.get('label', '') self.label = xml.get('label', '')
@ -306,7 +310,7 @@ class FormField(object):
self.require() self.require()
if xml.find('{jabber:x:data}desc') is not None: if xml.find('{jabber:x:data}desc') is not None:
self.setDescription(xml.find('{jabber:x:data}desc').text) self.setDescription(xml.find('{jabber:x:data}desc').text)
def getXML(self, ftype): def getXML(self, ftype):
field = ET.Element('{jabber:x:data}field') field = ET.Element('{jabber:x:data}field')
if ftype != 'result': if ftype != 'result':
@ -342,7 +346,7 @@ class FormField(object):
valuexml.text = value valuexml.text = value
field.append(valuexml) field.append(valuexml)
return field return field
def getXHTML(self): def getXHTML(self):
field = ET.Element('div', {'class': 'xmpp-xforms-%s' % self.type}) field = ET.Element('div', {'class': 'xmpp-xforms-%s' % self.type})
if self.label: if self.label:
@ -414,4 +418,4 @@ class FormField(object):
pass pass
label.append(formf) label.append(formf)
return field return field

View file

@ -16,6 +16,9 @@ from .. stanza.message import Message
import types import types
log = logging.getLogger(__name__)
class Form(ElementBase): class Form(ElementBase):
namespace = 'jabber:x:data' namespace = 'jabber:x:data'
name = 'x' name = 'x'
@ -33,7 +36,7 @@ class Form(ElementBase):
if title is not None: if title is not None:
self['title'] = title self['title'] = title
self.field = FieldAccessor(self) self.field = FieldAccessor(self)
def setup(self, xml=None): def setup(self, xml=None):
if ElementBase.setup(self, xml): #if we had to generate xml if ElementBase.setup(self, xml): #if we had to generate xml
self['type'] = 'form' self['type'] = 'form'
@ -55,11 +58,11 @@ class Form(ElementBase):
return field return field
def getXML(self, type='submit'): def getXML(self, type='submit'):
logging.warning("Form.getXML() is deprecated API compatibility with plugins/old_0004.py") log.warning("Form.getXML() is deprecated API compatibility with plugins/old_0004.py")
return self.xml return self.xml
def fromXML(self, xml): def fromXML(self, xml):
logging.warning("Form.fromXML() is deprecated API compatibility with plugins/old_0004.py") log.warning("Form.fromXML() is deprecated API compatibility with plugins/old_0004.py")
n = Form(xml=xml) n = Form(xml=xml)
return n return n
@ -113,10 +116,10 @@ class Form(ElementBase):
reportedXML = self.xml.find('{%s}reported' % self.namespace) reportedXML = self.xml.find('{%s}reported' % self.namespace)
if reportedXML is not None: if reportedXML is not None:
self.xml.remove(reportedXML) self.xml.remove(reportedXML)
def getFields(self, use_dict=False): def getFields(self, use_dict=False):
fields = {} if use_dict else [] fields = {} if use_dict else []
fieldsXML = self.xml.findall('{%s}field' % FormField.namespace) fieldsXML = self.xml.findall('{%s}field' % FormField.namespace)
for fieldXML in fieldsXML: for fieldXML in fieldsXML:
field = FormField(xml=fieldXML) field = FormField(xml=fieldXML)
if use_dict: if use_dict:
@ -144,7 +147,7 @@ class Form(ElementBase):
def getReported(self): def getReported(self):
fields = {} fields = {}
fieldsXML = self.xml.findall('{%s}reported/{%s}field' % (self.namespace, fieldsXML = self.xml.findall('{%s}reported/{%s}field' % (self.namespace,
FormField.namespace)) FormField.namespace))
for fieldXML in fieldsXML: for fieldXML in fieldsXML:
field = FormField(xml=fieldXML) field = FormField(xml=fieldXML)
@ -197,7 +200,7 @@ class Form(ElementBase):
fields = self.getFields(use_dict=True) fields = self.getFields(use_dict=True)
for field in values: for field in values:
fields[field]['value'] = values[field] fields[field]['value'] = values[field]
def merge(self, other): def merge(self, other):
new = copy.copy(self) new = copy.copy(self)
if type(other) == types.DictType: if type(other) == types.DictType:
@ -212,7 +215,7 @@ class Form(ElementBase):
class FieldAccessor(object): class FieldAccessor(object):
def __init__(self, form): def __init__(self, form):
self.form = form self.form = form
def __getitem__(self, key): def __getitem__(self, key):
return self.form.getFields(use_dict=True)[key] return self.form.getFields(use_dict=True)[key]
@ -366,21 +369,21 @@ class xep_0004(base.base_plugin):
self.xmpp.registerHandler( self.xmpp.registerHandler(
Callback('Data Form', Callback('Data Form',
MatchXPath('{%s}message/{%s}x' % (self.xmpp.default_ns, MatchXPath('{%s}message/{%s}x' % (self.xmpp.default_ns,
Form.namespace)), Form.namespace)),
self.handle_form)) self.handle_form))
registerStanzaPlugin(FormField, FieldOption) registerStanzaPlugin(FormField, FieldOption)
registerStanzaPlugin(Form, FormField) registerStanzaPlugin(Form, FormField)
registerStanzaPlugin(Message, Form) registerStanzaPlugin(Message, Form)
def makeForm(self, ftype='form', title='', instructions=''): def makeForm(self, ftype='form', title='', instructions=''):
f = Form() f = Form()
f['type'] = ftype f['type'] = ftype
f['title'] = title f['title'] = title
f['instructions'] = instructions f['instructions'] = instructions
return f return f
def post_init(self): def post_init(self):
base.base_plugin.post_init(self) base.base_plugin.post_init(self)
self.xmpp.plugin['xep_0030'].add_feature('jabber:x:data') self.xmpp.plugin['xep_0030'].add_feature('jabber:x:data')

View file

@ -16,6 +16,9 @@ from .. xmlstream.matcher.xpath import MatchXPath
from .. xmlstream import ElementBase, ET, JID, register_stanza_plugin from .. xmlstream import ElementBase, ET, JID, register_stanza_plugin
log = logging.getLogger(__name__)
class LastActivity(ElementBase): class LastActivity(ElementBase):
name = 'query' name = 'query'
namespace = 'jabber:iq:last' namespace = 'jabber:iq:last'
@ -68,10 +71,10 @@ class xep_0012(base.base_plugin):
def handle_last_activity_query(self, iq): def handle_last_activity_query(self, iq):
if iq['type'] == 'get': if iq['type'] == 'get':
logging.debug("Last activity requested by %s" % iq['from']) log.debug("Last activity requested by %s" % iq['from'])
self.xmpp.event('last_activity_request', iq) self.xmpp.event('last_activity_request', iq)
elif iq['type'] == 'result': elif iq['type'] == 'result':
logging.debug("Last activity result from %s" % iq['from']) log.debug("Last activity result from %s" % iq['from'])
self.xmpp.event('last_activity', iq) self.xmpp.event('last_activity', iq)
def handle_last_activity(self, iq): def handle_last_activity(self, iq):

View file

@ -13,315 +13,317 @@ from .. xmlstream.matcher.xpath import MatchXPath
from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID
from .. stanza.iq import Iq from .. stanza.iq import Iq
log = logging.getLogger(__name__)
class DiscoInfo(ElementBase): class DiscoInfo(ElementBase):
namespace = 'http://jabber.org/protocol/disco#info' namespace = 'http://jabber.org/protocol/disco#info'
name = 'query' name = 'query'
plugin_attrib = 'disco_info' plugin_attrib = 'disco_info'
interfaces = set(('node', 'features', 'identities')) interfaces = set(('node', 'features', 'identities'))
def getFeatures(self): def getFeatures(self):
features = [] features = []
featuresXML = self.xml.findall('{%s}feature' % self.namespace) featuresXML = self.xml.findall('{%s}feature' % self.namespace)
for feature in featuresXML: for feature in featuresXML:
features.append(feature.attrib['var']) features.append(feature.attrib['var'])
return features return features
def setFeatures(self, features): def setFeatures(self, features):
self.delFeatures() self.delFeatures()
for name in features: for name in features:
self.addFeature(name) self.addFeature(name)
def delFeatures(self): def delFeatures(self):
featuresXML = self.xml.findall('{%s}feature' % self.namespace) featuresXML = self.xml.findall('{%s}feature' % self.namespace)
for feature in featuresXML: for feature in featuresXML:
self.xml.remove(feature) self.xml.remove(feature)
def addFeature(self, feature): def addFeature(self, feature):
featureXML = ET.Element('{%s}feature' % self.namespace, featureXML = ET.Element('{%s}feature' % self.namespace,
{'var': feature}) {'var': feature})
self.xml.append(featureXML) self.xml.append(featureXML)
def delFeature(self, feature): def delFeature(self, feature):
featuresXML = self.xml.findall('{%s}feature' % self.namespace) featuresXML = self.xml.findall('{%s}feature' % self.namespace)
for featureXML in featuresXML: for featureXML in featuresXML:
if featureXML.attrib['var'] == feature: if featureXML.attrib['var'] == feature:
self.xml.remove(featureXML) self.xml.remove(featureXML)
def getIdentities(self): def getIdentities(self):
ids = [] ids = []
idsXML = self.xml.findall('{%s}identity' % self.namespace) idsXML = self.xml.findall('{%s}identity' % self.namespace)
for idXML in idsXML: for idXML in idsXML:
idData = (idXML.attrib['category'], idData = (idXML.attrib['category'],
idXML.attrib['type'], idXML.attrib['type'],
idXML.attrib.get('name', '')) idXML.attrib.get('name', ''))
ids.append(idData) ids.append(idData)
return ids return ids
def setIdentities(self, ids): def setIdentities(self, ids):
self.delIdentities() self.delIdentities()
for idData in ids: for idData in ids:
self.addIdentity(*idData) self.addIdentity(*idData)
def delIdentities(self): def delIdentities(self):
idsXML = self.xml.findall('{%s}identity' % self.namespace) idsXML = self.xml.findall('{%s}identity' % self.namespace)
for idXML in idsXML: for idXML in idsXML:
self.xml.remove(idXML) self.xml.remove(idXML)
def addIdentity(self, category, id_type, name=''): def addIdentity(self, category, id_type, name=''):
idXML = ET.Element('{%s}identity' % self.namespace, idXML = ET.Element('{%s}identity' % self.namespace,
{'category': category, {'category': category,
'type': id_type, 'type': id_type,
'name': name}) 'name': name})
self.xml.append(idXML) self.xml.append(idXML)
def delIdentity(self, category, id_type, name=''): def delIdentity(self, category, id_type, name=''):
idsXML = self.xml.findall('{%s}identity' % self.namespace) idsXML = self.xml.findall('{%s}identity' % self.namespace)
for idXML in idsXML: for idXML in idsXML:
idData = (idXML.attrib['category'], idData = (idXML.attrib['category'],
idXML.attrib['type']) idXML.attrib['type'])
delId = (category, id_type) delId = (category, id_type)
if idData == delId: if idData == delId:
self.xml.remove(idXML) self.xml.remove(idXML)
class DiscoItems(ElementBase): class DiscoItems(ElementBase):
namespace = 'http://jabber.org/protocol/disco#items' namespace = 'http://jabber.org/protocol/disco#items'
name = 'query' name = 'query'
plugin_attrib = 'disco_items' plugin_attrib = 'disco_items'
interfaces = set(('node', 'items')) interfaces = set(('node', 'items'))
def getItems(self): def getItems(self):
items = [] items = []
itemsXML = self.xml.findall('{%s}item' % self.namespace) itemsXML = self.xml.findall('{%s}item' % self.namespace)
for item in itemsXML: for item in itemsXML:
itemData = (item.attrib['jid'], itemData = (item.attrib['jid'],
item.attrib.get('node'), item.attrib.get('node'),
item.attrib.get('name')) item.attrib.get('name'))
items.append(itemData) items.append(itemData)
return items return items
def setItems(self, items): def setItems(self, items):
self.delItems() self.delItems()
for item in items: for item in items:
self.addItem(*item) self.addItem(*item)
def delItems(self): def delItems(self):
itemsXML = self.xml.findall('{%s}item' % self.namespace) itemsXML = self.xml.findall('{%s}item' % self.namespace)
for item in itemsXML: for item in itemsXML:
self.xml.remove(item) self.xml.remove(item)
def addItem(self, jid, node='', name=''): def addItem(self, jid, node='', name=''):
itemXML = ET.Element('{%s}item' % self.namespace, {'jid': jid}) itemXML = ET.Element('{%s}item' % self.namespace, {'jid': jid})
if name: if name:
itemXML.attrib['name'] = name itemXML.attrib['name'] = name
if node: if node:
itemXML.attrib['node'] = node itemXML.attrib['node'] = node
self.xml.append(itemXML) self.xml.append(itemXML)
def delItem(self, jid, node=''):
itemsXML = self.xml.findall('{%s}item' % self.namespace)
for itemXML in itemsXML:
itemData = (itemXML.attrib['jid'],
itemXML.attrib.get('node', ''))
itemDel = (jid, node)
if itemData == itemDel:
self.xml.remove(itemXML)
def delItem(self, jid, node=''):
itemsXML = self.xml.findall('{%s}item' % self.namespace)
for itemXML in itemsXML:
itemData = (itemXML.attrib['jid'],
itemXML.attrib.get('node', ''))
itemDel = (jid, node)
if itemData == itemDel:
self.xml.remove(itemXML)
class DiscoNode(object): class DiscoNode(object):
""" """
Collection object for grouping info and item information Collection object for grouping info and item information
into nodes. into nodes.
""" """
def __init__(self, name): def __init__(self, name):
self.name = name self.name = name
self.info = DiscoInfo() self.info = DiscoInfo()
self.items = DiscoItems() self.items = DiscoItems()
self.info['node'] = name self.info['node'] = name
self.items['node'] = name self.items['node'] = name
# This is a bit like poor man's inheritance, but # This is a bit like poor man's inheritance, but
# to simplify adding information to the node we # to simplify adding information to the node we
# map node functions to either the info or items # map node functions to either the info or items
# stanza objects. # stanza objects.
# #
# We don't want to make DiscoNode inherit from # We don't want to make DiscoNode inherit from
# DiscoInfo and DiscoItems because DiscoNode is # DiscoInfo and DiscoItems because DiscoNode is
# not an actual stanza, and doing so would create # not an actual stanza, and doing so would create
# confusion and potential bugs. # confusion and potential bugs.
self._map(self.items, 'items', ['get', 'set', 'del']) self._map(self.items, 'items', ['get', 'set', 'del'])
self._map(self.items, 'item', ['add', 'del']) self._map(self.items, 'item', ['add', 'del'])
self._map(self.info, 'identities', ['get', 'set', 'del']) self._map(self.info, 'identities', ['get', 'set', 'del'])
self._map(self.info, 'identity', ['add', 'del']) self._map(self.info, 'identity', ['add', 'del'])
self._map(self.info, 'features', ['get', 'set', 'del']) self._map(self.info, 'features', ['get', 'set', 'del'])
self._map(self.info, 'feature', ['add', 'del']) self._map(self.info, 'feature', ['add', 'del'])
def isEmpty(self): def isEmpty(self):
""" """
Test if the node contains any information. Useful for Test if the node contains any information. Useful for
determining if a node can be deleted. determining if a node can be deleted.
""" """
ids = self.getIdentities() ids = self.getIdentities()
features = self.getFeatures() features = self.getFeatures()
items = self.getItems() items = self.getItems()
if not ids and not features and not items: if not ids and not features and not items:
return True return True
return False return False
def _map(self, obj, interface, access): def _map(self, obj, interface, access):
""" """
Map functions of the form obj.accessInterface Map functions of the form obj.accessInterface
to self.accessInterface for each given access type. to self.accessInterface for each given access type.
""" """
interface = interface.title() interface = interface.title()
for access_type in access: for access_type in access:
method = access_type + interface method = access_type + interface
if hasattr(obj, method): if hasattr(obj, method):
setattr(self, method, getattr(obj, method)) setattr(self, method, getattr(obj, method))
class xep_0030(base.base_plugin): class xep_0030(base.base_plugin):
""" """
XEP-0030 Service Discovery XEP-0030 Service Discovery
""" """
def plugin_init(self):
self.xep = '0030'
self.description = 'Service Discovery'
self.xmpp.registerHandler( def plugin_init(self):
Callback('Disco Items', self.xep = '0030'
MatchXPath('{%s}iq/{%s}query' % (self.xmpp.default_ns, self.description = 'Service Discovery'
DiscoItems.namespace)),
self.handle_item_query))
self.xmpp.registerHandler( self.xmpp.registerHandler(
Callback('Disco Info', Callback('Disco Items',
MatchXPath('{%s}iq/{%s}query' % (self.xmpp.default_ns, MatchXPath('{%s}iq/{%s}query' % (self.xmpp.default_ns,
DiscoInfo.namespace)), DiscoItems.namespace)),
self.handle_info_query)) self.handle_item_query))
registerStanzaPlugin(Iq, DiscoInfo) self.xmpp.registerHandler(
registerStanzaPlugin(Iq, DiscoItems) Callback('Disco Info',
MatchXPath('{%s}iq/{%s}query' % (self.xmpp.default_ns,
DiscoInfo.namespace)),
self.handle_info_query))
self.xmpp.add_event_handler('disco_items_request', self.handle_disco_items) registerStanzaPlugin(Iq, DiscoInfo)
self.xmpp.add_event_handler('disco_info_request', self.handle_disco_info) registerStanzaPlugin(Iq, DiscoItems)
self.nodes = {'main': DiscoNode('main')} self.xmpp.add_event_handler('disco_items_request', self.handle_disco_items)
self.xmpp.add_event_handler('disco_info_request', self.handle_disco_info)
def add_node(self, node): self.nodes = {'main': DiscoNode('main')}
if node not in self.nodes:
self.nodes[node] = DiscoNode(node)
def del_node(self, node): def add_node(self, node):
if node in self.nodes: if node not in self.nodes:
del self.nodes[node] self.nodes[node] = DiscoNode(node)
def handle_item_query(self, iq): def del_node(self, node):
if iq['type'] == 'get': if node in self.nodes:
logging.debug("Items requested by %s" % iq['from']) del self.nodes[node]
self.xmpp.event('disco_items_request', iq)
elif iq['type'] == 'result':
logging.debug("Items result from %s" % iq['from'])
self.xmpp.event('disco_items', iq)
def handle_info_query(self, iq): def handle_item_query(self, iq):
if iq['type'] == 'get': if iq['type'] == 'get':
logging.debug("Info requested by %s" % iq['from']) log.debug("Items requested by %s" % iq['from'])
self.xmpp.event('disco_info_request', iq) self.xmpp.event('disco_items_request', iq)
elif iq['type'] == 'result': elif iq['type'] == 'result':
logging.debug("Info result from %s" % iq['from']) log.debug("Items result from %s" % iq['from'])
self.xmpp.event('disco_info', iq) self.xmpp.event('disco_items', iq)
def handle_disco_info(self, iq, forwarded=False): def handle_info_query(self, iq):
""" if iq['type'] == 'get':
A default handler for disco#info requests. If another log.debug("Info requested by %s" % iq['from'])
handler is registered, this one will defer and not run. self.xmpp.event('disco_info_request', iq)
""" elif iq['type'] == 'result':
handlers = self.xmpp.event_handlers['disco_info_request'] log.debug("Info result from %s" % iq['from'])
if not forwarded and len(handlers) > 1: self.xmpp.event('disco_info', iq)
return
node_name = iq['disco_info']['node'] def handle_disco_info(self, iq, forwarded=False):
if not node_name: """
node_name = 'main' A default handler for disco#info requests. If another
handler is registered, this one will defer and not run.
"""
if not forwarded and self.xmpp.event_handled('disco_info_request'):
return
logging.debug("Using default handler for disco#info on node '%s'." % node_name) node_name = iq['disco_info']['node']
if not node_name:
node_name = 'main'
if node_name in self.nodes: log.debug("Using default handler for disco#info on node '%s'." % node_name)
node = self.nodes[node_name]
iq.reply().setPayload(node.info.xml).send()
else:
logging.debug("Node %s requested, but does not exist." % node_name)
iq.reply().error().setPayload(iq['disco_info'].xml)
iq['error']['code'] = '404'
iq['error']['type'] = 'cancel'
iq['error']['condition'] = 'item-not-found'
iq.send()
def handle_disco_items(self, iq, forwarded=False):
"""
A default handler for disco#items requests. If another
handler is registered, this one will defer and not run.
If this handler is called by your own custom handler with if node_name in self.nodes:
forwarded set to True, then it will run as normal. node = self.nodes[node_name]
""" iq.reply().setPayload(node.info.xml).send()
handlers = self.xmpp.event_handlers['disco_items_request'] else:
if not forwarded and len(handlers) > 1: log.debug("Node %s requested, but does not exist." % node_name)
return iq.reply().error().setPayload(iq['disco_info'].xml)
iq['error']['code'] = '404'
iq['error']['type'] = 'cancel'
iq['error']['condition'] = 'item-not-found'
iq.send()
node_name = iq['disco_items']['node'] def handle_disco_items(self, iq, forwarded=False):
if not node_name: """
node_name = 'main' A default handler for disco#items requests. If another
handler is registered, this one will defer and not run.
logging.debug("Using default handler for disco#items on node '%s'." % node_name) If this handler is called by your own custom handler with
forwarded set to True, then it will run as normal.
"""
if not forwarded and self.xmpp.event_handled('disco_items_request'):
return
if node_name in self.nodes: node_name = iq['disco_items']['node']
node = self.nodes[node_name] if not node_name:
iq.reply().setPayload(node.items.xml).send() node_name = 'main'
else:
logging.debug("Node %s requested, but does not exist." % node_name)
iq.reply().error().setPayload(iq['disco_items'].xml)
iq['error']['code'] = '404'
iq['error']['type'] = 'cancel'
iq['error']['condition'] = 'item-not-found'
iq.send()
# Older interface methods for backwards compatibility log.debug("Using default handler for disco#items on node '%s'." % node_name)
def getInfo(self, jid, node='', dfrom=None): if node_name in self.nodes:
iq = self.xmpp.Iq() node = self.nodes[node_name]
iq['type'] = 'get' iq.reply().setPayload(node.items.xml).send()
iq['to'] = jid else:
iq['from'] = dfrom log.debug("Node %s requested, but does not exist." % node_name)
iq['disco_info']['node'] = node iq.reply().error().setPayload(iq['disco_items'].xml)
return iq.send() iq['error']['code'] = '404'
iq['error']['type'] = 'cancel'
iq['error']['condition'] = 'item-not-found'
iq.send()
def getItems(self, jid, node='', dfrom=None): # Older interface methods for backwards compatibility
iq = self.xmpp.Iq()
iq['type'] = 'get' def getInfo(self, jid, node='', dfrom=None):
iq['to'] = jid iq = self.xmpp.Iq()
iq['from'] = dfrom iq['type'] = 'get'
iq['disco_items']['node'] = node iq['to'] = jid
return iq.send() iq['from'] = dfrom
iq['disco_info']['node'] = node
def add_feature(self, feature, node='main'): return iq.send()
self.add_node(node)
self.nodes[node].addFeature(feature) def getItems(self, jid, node='', dfrom=None):
iq = self.xmpp.Iq()
def add_identity(self, category='', itype='', name='', node='main'): iq['type'] = 'get'
self.add_node(node) iq['to'] = jid
self.nodes[node].addIdentity(category=category, iq['from'] = dfrom
id_type=itype, iq['disco_items']['node'] = node
name=name) return iq.send()
def add_item(self, jid=None, name='', node='main', subnode=''): def add_feature(self, feature, node='main'):
self.add_node(node) self.add_node(node)
self.add_node(subnode) self.nodes[node].addFeature(feature)
if jid is None:
jid = self.xmpp.fulljid def add_identity(self, category='', itype='', name='', node='main'):
self.nodes[node].addItem(jid=jid, name=name, node=subnode) self.add_node(node)
self.nodes[node].addIdentity(category=category,
id_type=itype,
name=name)
def add_item(self, jid=None, name='', node='main', subnode=''):
self.add_node(node)
self.add_node(subnode)
if jid is None:
jid = self.xmpp.fulljid
self.nodes[node].addItem(jid=jid, name=name, node=subnode)

View file

@ -2,7 +2,7 @@
SleekXMPP: The Sleek XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
from __future__ import with_statement from __future__ import with_statement
@ -15,6 +15,10 @@ from .. xmlstream.handler.callback import Callback
from .. xmlstream.matcher.xpath import MatchXPath from .. xmlstream.matcher.xpath import MatchXPath
from .. xmlstream.matcher.xmlmask import MatchXMLMask from .. xmlstream.matcher.xmlmask import MatchXMLMask
log = logging.getLogger(__name__)
class MUCPresence(ElementBase): class MUCPresence(ElementBase):
name = 'x' name = 'x'
namespace = 'http://jabber.org/protocol/muc#user' namespace = 'http://jabber.org/protocol/muc#user'
@ -34,79 +38,79 @@ class MUCPresence(ElementBase):
#TODO if no affilation, set it to the default and return default #TODO if no affilation, set it to the default and return default
item = self.getXMLItem() item = self.getXMLItem()
return item.get('affiliation', '') return item.get('affiliation', '')
def setAffiliation(self, value): def setAffiliation(self, value):
item = self.getXMLItem() item = self.getXMLItem()
#TODO check for valid affiliation #TODO check for valid affiliation
item.attrib['affiliation'] = value item.attrib['affiliation'] = value
return self return self
def delAffiliation(self): def delAffiliation(self):
item = self.getXMLItem() item = self.getXMLItem()
#TODO set default affiliation #TODO set default affiliation
if 'affiliation' in item.attrib: del item.attrib['affiliation'] if 'affiliation' in item.attrib: del item.attrib['affiliation']
return self return self
def getJid(self): def getJid(self):
item = self.getXMLItem() item = self.getXMLItem()
return JID(item.get('jid', '')) return JID(item.get('jid', ''))
def setJid(self, value): def setJid(self, value):
item = self.getXMLItem() item = self.getXMLItem()
if not isinstance(value, str): if not isinstance(value, str):
value = str(value) value = str(value)
item.attrib['jid'] = value item.attrib['jid'] = value
return self return self
def delJid(self): def delJid(self):
item = self.getXMLItem() item = self.getXMLItem()
if 'jid' in item.attrib: del item.attrib['jid'] if 'jid' in item.attrib: del item.attrib['jid']
return self return self
def getRole(self): def getRole(self):
item = self.getXMLItem() item = self.getXMLItem()
#TODO get default role, set default role if none #TODO get default role, set default role if none
return item.get('role', '') return item.get('role', '')
def setRole(self, value): def setRole(self, value):
item = self.getXMLItem() item = self.getXMLItem()
#TODO check for valid role #TODO check for valid role
item.attrib['role'] = value item.attrib['role'] = value
return self return self
def delRole(self): def delRole(self):
item = self.getXMLItem() item = self.getXMLItem()
#TODO set default role #TODO set default role
if 'role' in item.attrib: del item.attrib['role'] if 'role' in item.attrib: del item.attrib['role']
return self return self
def getNick(self): def getNick(self):
return self.parent()['from'].resource return self.parent()['from'].resource
def getRoom(self): def getRoom(self):
return self.parent()['from'].bare return self.parent()['from'].bare
def setNick(self, value): def setNick(self, value):
logging.warning("Cannot set nick through mucpresence plugin.") log.warning("Cannot set nick through mucpresence plugin.")
return self return self
def setRoom(self, value): def setRoom(self, value):
logging.warning("Cannot set room through mucpresence plugin.") log.warning("Cannot set room through mucpresence plugin.")
return self return self
def delNick(self): def delNick(self):
logging.warning("Cannot delete nick through mucpresence plugin.") log.warning("Cannot delete nick through mucpresence plugin.")
return self return self
def delRoom(self): def delRoom(self):
logging.warning("Cannot delete room through mucpresence plugin.") log.warning("Cannot delete room through mucpresence plugin.")
return self return self
class xep_0045(base.base_plugin): class xep_0045(base.base_plugin):
""" """
Impliments XEP-0045 Multi User Chat Impliments XEP-0045 Multi User Chat
""" """
def plugin_init(self): def plugin_init(self):
self.rooms = {} self.rooms = {}
self.ourNicks = {} self.ourNicks = {}
@ -116,7 +120,8 @@ class xep_0045(base.base_plugin):
registerStanzaPlugin(Presence, MUCPresence) registerStanzaPlugin(Presence, MUCPresence)
self.xmpp.registerHandler(Callback('MUCPresence', MatchXMLMask("<presence xmlns='%s' />" % self.xmpp.default_ns), self.handle_groupchat_presence)) self.xmpp.registerHandler(Callback('MUCPresence', MatchXMLMask("<presence xmlns='%s' />" % self.xmpp.default_ns), self.handle_groupchat_presence))
self.xmpp.registerHandler(Callback('MUCMessage', MatchXMLMask("<message xmlns='%s' type='groupchat'><body/></message>" % self.xmpp.default_ns), self.handle_groupchat_message)) self.xmpp.registerHandler(Callback('MUCMessage', MatchXMLMask("<message xmlns='%s' type='groupchat'><body/></message>" % self.xmpp.default_ns), self.handle_groupchat_message))
self.xmpp.registerHandler(Callback('MUCSubject', MatchXMLMask("<message xmlns='%s' type='groupchat'><subject/></message>" % self.xmpp.default_ns), self.handle_groupchat_subject))
def handle_groupchat_presence(self, pr): def handle_groupchat_presence(self, pr):
""" Handle a presence in a muc. """ Handle a presence in a muc.
""" """
@ -135,27 +140,33 @@ class xep_0045(base.base_plugin):
if entry['nick'] not in self.rooms[entry['room']]: if entry['nick'] not in self.rooms[entry['room']]:
got_online = True got_online = True
self.rooms[entry['room']][entry['nick']] = entry self.rooms[entry['room']][entry['nick']] = entry
logging.debug("MUC presence from %s/%s : %s" % (entry['room'],entry['nick'], entry)) log.debug("MUC presence from %s/%s : %s" % (entry['room'],entry['nick'], entry))
self.xmpp.event("groupchat_presence", pr) self.xmpp.event("groupchat_presence", pr)
self.xmpp.event("muc::%s::presence" % entry['room'], pr) self.xmpp.event("muc::%s::presence" % entry['room'], pr)
if got_offline: if got_offline:
self.xmpp.event("muc::%s::got_offline" % entry['room'], pr) self.xmpp.event("muc::%s::got_offline" % entry['room'], pr)
if got_online: if got_online:
self.xmpp.event("muc::%s::got_online" % entry['room'], pr) self.xmpp.event("muc::%s::got_online" % entry['room'], pr)
def handle_groupchat_message(self, msg): def handle_groupchat_message(self, msg):
""" Handle a message event in a muc. """ Handle a message event in a muc.
""" """
self.xmpp.event('groupchat_message', msg) self.xmpp.event('groupchat_message', msg)
self.xmpp.event("muc::%s::message" % msg['from'].bare, msg) self.xmpp.event("muc::%s::message" % msg['from'].bare, msg)
def handle_groupchat_subject(self, msg):
""" Handle a message coming from a muc indicating
a change of subject (or announcing it when joining the room)
"""
self.xmpp.event('groupchat_subject', msg)
def jidInRoom(self, room, jid): def jidInRoom(self, room, jid):
for nick in self.rooms[room]: for nick in self.rooms[room]:
entry = self.rooms[room][nick] entry = self.rooms[room][nick]
if entry is not None and entry['jid'].full == jid: if entry is not None and entry['jid'].full == jid:
return True return True
return False return False
def getNick(self, room, jid): def getNick(self, room, jid):
for nick in self.rooms[room]: for nick in self.rooms[room]:
entry = self.rooms[room][nick] entry = self.rooms[room][nick]
@ -176,12 +187,12 @@ class xep_0045(base.base_plugin):
if xform is None: return False if xform is None: return False
form = self.xmpp.plugin['old_0004'].buildForm(xform) form = self.xmpp.plugin['old_0004'].buildForm(xform)
return form return form
def configureRoom(self, room, form=None, ifrom=None): def configureRoom(self, room, form=None, ifrom=None):
if form is None: if form is None:
form = self.getRoomForm(room, ifrom=ifrom) form = self.getRoomForm(room, ifrom=ifrom)
#form = self.xmpp.plugin['old_0004'].makeForm(ftype='submit') #form = self.xmpp.plugin['old_0004'].makeForm(ftype='submit')
#form.addField('FORM_TYPE', value='http://jabber.org/protocol/muc#roomconfig') #form.addField('FORM_TYPE', value='http://jabber.org/protocol/muc#roomconfig')
iq = self.xmpp.makeIqSet() iq = self.xmpp.makeIqSet()
iq['to'] = room iq['to'] = room
if ifrom is not None: if ifrom is not None:
@ -194,7 +205,7 @@ class xep_0045(base.base_plugin):
if result['type'] == 'error': if result['type'] == 'error':
return False return False
return True return True
def joinMUC(self, room, nick, maxhistory="0", password='', wait=False, pstatus=None, pshow=None): def joinMUC(self, room, nick, maxhistory="0", password='', wait=False, pstatus=None, pshow=None):
""" Join the specified room, requesting 'maxhistory' lines of history. """ Join the specified room, requesting 'maxhistory' lines of history.
""" """
@ -220,7 +231,7 @@ class xep_0045(base.base_plugin):
self.xmpp.send(stanza, expect) self.xmpp.send(stanza, expect)
self.rooms[room] = {} self.rooms[room] = {}
self.ourNicks[room] = nick self.ourNicks[room] = nick
def destroy(self, room, reason='', altroom = '', ifrom=None): def destroy(self, room, reason='', altroom = '', ifrom=None):
iq = self.xmpp.makeIqSet() iq = self.xmpp.makeIqSet()
if ifrom is not None: if ifrom is not None:
@ -246,9 +257,9 @@ class xep_0045(base.base_plugin):
raise TypeError raise TypeError
query = ET.Element('{http://jabber.org/protocol/muc#admin}query') query = ET.Element('{http://jabber.org/protocol/muc#admin}query')
if nick is not None: if nick is not None:
item = ET.Element('item', {'affiliation':affiliation, 'nick':nick}) item = ET.Element('item', {'affiliation':affiliation, 'nick':nick})
else: else:
item = ET.Element('item', {'affiliation':affiliation, 'jid':jid}) item = ET.Element('item', {'affiliation':affiliation, 'jid':jid})
query.append(item) query.append(item)
iq = self.xmpp.makeIqSet(query) iq = self.xmpp.makeIqSet(query)
iq['to'] = room iq['to'] = room
@ -256,7 +267,7 @@ class xep_0045(base.base_plugin):
if result is False or result['type'] != 'result': if result is False or result['type'] != 'result':
raise ValueError raise ValueError
return True return True
def invite(self, room, jid, reason=''): def invite(self, room, jid, reason=''):
""" Invite a jid to a room.""" """ Invite a jid to a room."""
msg = self.xmpp.makeMessage(room) msg = self.xmpp.makeMessage(room)
@ -279,7 +290,7 @@ class xep_0045(base.base_plugin):
else: else:
self.xmpp.sendPresence(pshow='unavailable', pto="%s/%s" % (room, nick)) self.xmpp.sendPresence(pshow='unavailable', pto="%s/%s" % (room, nick))
del self.rooms[room] del self.rooms[room]
def getRoomConfig(self, room): def getRoomConfig(self, room):
iq = self.xmpp.makeIqGet('http://jabber.org/protocol/muc#owner') iq = self.xmpp.makeIqGet('http://jabber.org/protocol/muc#owner')
iq['to'] = room iq['to'] = room
@ -291,14 +302,14 @@ class xep_0045(base.base_plugin):
if form is None: if form is None:
raise ValueError raise ValueError
return self.xmpp.plugin['xep_0004'].buildForm(form) return self.xmpp.plugin['xep_0004'].buildForm(form)
def cancelConfig(self, room): def cancelConfig(self, room):
query = ET.Element('{http://jabber.org/protocol/muc#owner}query') query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
x = ET.Element('{jabber:x:data}x', type='cancel') x = ET.Element('{jabber:x:data}x', type='cancel')
query.append(x) query.append(x)
iq = self.xmpp.makeIqSet(query) iq = self.xmpp.makeIqSet(query)
iq.send() iq.send()
def setRoomConfig(self, room, config): def setRoomConfig(self, room, config):
query = ET.Element('{http://jabber.org/protocol/muc#owner}query') query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
x = config.getXML('submit') x = config.getXML('submit')
@ -307,15 +318,15 @@ class xep_0045(base.base_plugin):
iq['to'] = room iq['to'] = room
iq['from'] = self.xmpp.jid iq['from'] = self.xmpp.jid
iq.send() iq.send()
def getJoinedRooms(self): def getJoinedRooms(self):
return self.rooms.keys() return self.rooms.keys()
def getOurJidInRoom(self, roomJid): def getOurJidInRoom(self, roomJid):
""" Return the jid we're using in a room. """ Return the jid we're using in a room.
""" """
return "%s/%s" % (roomJid, self.ourNicks[roomJid]) return "%s/%s" % (roomJid, self.ourNicks[roomJid])
def getJidProperty(self, room, nick, jidProperty): def getJidProperty(self, room, nick, jidProperty):
""" Get the property of a nick in a room, such as its 'jid' or 'affiliation' """ Get the property of a nick in a room, such as its 'jid' or 'affiliation'
If not found, return None. If not found, return None.
@ -324,7 +335,7 @@ class xep_0045(base.base_plugin):
return self.rooms[room][nick][jidProperty] return self.rooms[room][nick][jidProperty]
else: else:
return None return None
def getRoster(self, room): def getRoster(self, room):
""" Get the list of nicks in a room. """ Get the list of nicks in a room.
""" """

View file

@ -6,6 +6,10 @@ from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET
from . import stanza_pubsub from . import stanza_pubsub
from . xep_0004 import Form from . xep_0004 import Form
log = logging.getLogger(__name__)
class xep_0060(base.base_plugin): class xep_0060(base.base_plugin):
""" """
XEP-0060 Publish Subscribe XEP-0060 Publish Subscribe
@ -14,7 +18,7 @@ class xep_0060(base.base_plugin):
def plugin_init(self): def plugin_init(self):
self.xep = '0060' self.xep = '0060'
self.description = 'Publish-Subscribe' self.description = 'Publish-Subscribe'
def create_node(self, jid, node, config=None, collection=False, ntype=None): def create_node(self, jid, node, config=None, collection=False, ntype=None):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub') pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub')
create = ET.Element('create') create = ET.Element('create')
@ -52,7 +56,7 @@ class xep_0060(base.base_plugin):
result = iq.send() result = iq.send()
if result is False or result is None or result['type'] == 'error': return False if result is False or result is None or result['type'] == 'error': return False
return True return True
def subscribe(self, jid, node, bare=True, subscribee=None): def subscribe(self, jid, node, bare=True, subscribee=None):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub') pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub')
subscribe = ET.Element('subscribe') subscribe = ET.Element('subscribe')
@ -72,7 +76,7 @@ class xep_0060(base.base_plugin):
result = iq.send() result = iq.send()
if result is False or result is None or result['type'] == 'error': return False if result is False or result is None or result['type'] == 'error': return False
return True return True
def unsubscribe(self, jid, node, bare=True, subscribee=None): def unsubscribe(self, jid, node, bare=True, subscribee=None):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub') pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub')
unsubscribe = ET.Element('unsubscribe') unsubscribe = ET.Element('unsubscribe')
@ -92,7 +96,7 @@ class xep_0060(base.base_plugin):
result = iq.send() result = iq.send()
if result is False or result is None or result['type'] == 'error': return False if result is False or result is None or result['type'] == 'error': return False
return True return True
def getNodeConfig(self, jid, node=None): # if no node, then grab default def getNodeConfig(self, jid, node=None): # if no node, then grab default
pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub') pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub')
if node is not None: if node is not None:
@ -110,17 +114,17 @@ class xep_0060(base.base_plugin):
#self.xmpp.add_handler("<iq id='%s'/>" % id, self.handlerCreateNodeResponse) #self.xmpp.add_handler("<iq id='%s'/>" % id, self.handlerCreateNodeResponse)
result = iq.send() result = iq.send()
if result is None or result == False or result['type'] == 'error': if result is None or result == False or result['type'] == 'error':
logging.warning("got error instead of config") log.warning("got error instead of config")
return False return False
if node is not None: if node is not None:
form = result.find('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}configure/{jabber:x:data}x') form = result.find('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}configure/{jabber:x:data}x')
else: else:
form = result.find('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}default/{jabber:x:data}x') form = result.find('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}default/{jabber:x:data}x')
if not form or form is None: if not form or form is None:
logging.error("No form found.") log.error("No form found.")
return False return False
return Form(xml=form) return Form(xml=form)
def getNodeSubscriptions(self, jid, node): def getNodeSubscriptions(self, jid, node):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub') pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub')
subscriptions = ET.Element('subscriptions') subscriptions = ET.Element('subscriptions')
@ -133,7 +137,7 @@ class xep_0060(base.base_plugin):
id = iq['id'] id = iq['id']
result = iq.send() result = iq.send()
if result is None or result == False or result['type'] == 'error': if result is None or result == False or result['type'] == 'error':
logging.warning("got error instead of config") log.warning("got error instead of config")
return False return False
else: else:
results = result.findall('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}subscriptions/{http://jabber.org/protocol/pubsub#owner}subscription') results = result.findall('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}subscriptions/{http://jabber.org/protocol/pubsub#owner}subscription')
@ -156,7 +160,7 @@ class xep_0060(base.base_plugin):
id = iq['id'] id = iq['id']
result = iq.send() result = iq.send()
if result is None or result == False or result['type'] == 'error': if result is None or result == False or result['type'] == 'error':
logging.warning("got error instead of config") log.warning("got error instead of config")
return False return False
else: else:
results = result.findall('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}affiliations/{http://jabber.org/protocol/pubsub#owner}affiliation') results = result.findall('{http://jabber.org/protocol/pubsub#owner}pubsub/{http://jabber.org/protocol/pubsub#owner}affiliations/{http://jabber.org/protocol/pubsub#owner}affiliation')
@ -181,8 +185,8 @@ class xep_0060(base.base_plugin):
return True return True
else: else:
return False return False
def setNodeConfig(self, jid, node, config): def setNodeConfig(self, jid, node, config):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub') pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub')
configure = ET.Element('configure') configure = ET.Element('configure')
@ -195,10 +199,10 @@ class xep_0060(base.base_plugin):
iq.attrib['from'] = self.xmpp.fulljid iq.attrib['from'] = self.xmpp.fulljid
id = iq['id'] id = iq['id']
result = iq.send() result = iq.send()
if result is None or result['type'] == 'error': if result is None or result['type'] == 'error':
return False return False
return True return True
def setItem(self, jid, node, items=[]): def setItem(self, jid, node, items=[]):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub') pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub')
publish = ET.Element('publish') publish = ET.Element('publish')
@ -218,7 +222,7 @@ class xep_0060(base.base_plugin):
result = iq.send() result = iq.send()
if result is None or result is False or result['type'] == 'error': return False if result is None or result is False or result['type'] == 'error': return False
return True return True
def addItem(self, jid, node, items=[]): def addItem(self, jid, node, items=[]):
return self.setItem(jid, node, items) return self.setItem(jid, node, items)
@ -237,7 +241,7 @@ class xep_0060(base.base_plugin):
result = iq.send() result = iq.send()
if result is None or result is False or result['type'] == 'error': return False if result is None or result is False or result['type'] == 'error': return False
return True return True
def getNodes(self, jid): def getNodes(self, jid):
response = self.xmpp.plugin['xep_0030'].getItems(jid) response = self.xmpp.plugin['xep_0030'].getItems(jid)
items = response.findall('{http://jabber.org/protocol/disco#items}query/{http://jabber.org/protocol/disco#items}item') items = response.findall('{http://jabber.org/protocol/disco#items}query/{http://jabber.org/protocol/disco#items}item')
@ -246,7 +250,7 @@ class xep_0060(base.base_plugin):
for item in items: for item in items:
nodes[item.get('node')] = item.get('name') nodes[item.get('node')] = item.get('name')
return nodes return nodes
def getItems(self, jid, node): def getItems(self, jid, node):
response = self.xmpp.plugin['xep_0030'].getItems(jid, node) response = self.xmpp.plugin['xep_0030'].getItems(jid, node)
items = response.findall('{http://jabber.org/protocol/disco#items}query/{http://jabber.org/protocol/disco#items}item') items = response.findall('{http://jabber.org/protocol/disco#items}query/{http://jabber.org/protocol/disco#items}item')
@ -264,7 +268,7 @@ class xep_0060(base.base_plugin):
try: try:
config.field['pubsub#collection'].setValue(parent) config.field['pubsub#collection'].setValue(parent)
except KeyError: except KeyError:
logging.warning("pubsub#collection doesn't exist in config, trying to add it") log.warning("pubsub#collection doesn't exist in config, trying to add it")
config.addField('pubsub#collection', value=parent) config.addField('pubsub#collection', value=parent)
if not self.setNodeConfig(jid, child, config): if not self.setNodeConfig(jid, child, config):
return False return False
@ -298,7 +302,7 @@ class xep_0060(base.base_plugin):
try: try:
config.field['pubsub#collection'].setValue(parent) config.field['pubsub#collection'].setValue(parent)
except KeyError: except KeyError:
logging.warning("pubsub#collection doesn't exist in config, trying to add it") log.warning("pubsub#collection doesn't exist in config, trying to add it")
config.addField('pubsub#collection', value=parent) config.addField('pubsub#collection', value=parent)
if not self.setNodeConfig(jid, child, config): if not self.setNodeConfig(jid, child, config):
return False return False

View file

@ -2,7 +2,7 @@
SleekXMPP: The Sleek XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
from __future__ import with_statement from __future__ import with_statement
@ -12,6 +12,9 @@ import hashlib
from . import base from . import base
log = logging.getLogger(__name__)
class xep_0078(base.base_plugin): class xep_0078(base.base_plugin):
""" """
XEP-0078 NON-SASL Authentication XEP-0078 NON-SASL Authentication
@ -23,14 +26,14 @@ class xep_0078(base.base_plugin):
#disabling until I fix conflict with PLAIN #disabling until I fix conflict with PLAIN
#self.xmpp.registerFeature("<auth xmlns='http://jabber.org/features/iq-auth'/>", self.auth) #self.xmpp.registerFeature("<auth xmlns='http://jabber.org/features/iq-auth'/>", self.auth)
self.streamid = '' self.streamid = ''
def check_stream(self, xml): def check_stream(self, xml):
self.streamid = xml.attrib['id'] self.streamid = xml.attrib['id']
if xml.get('version', '0') != '1.0': if xml.get('version', '0') != '1.0':
self.auth() self.auth()
def auth(self, xml=None): def auth(self, xml=None):
logging.debug("Starting jabber:iq:auth Authentication") log.debug("Starting jabber:iq:auth Authentication")
auth_request = self.xmpp.makeIqGet() auth_request = self.xmpp.makeIqGet()
auth_request_query = ET.Element('{jabber:iq:auth}query') auth_request_query = ET.Element('{jabber:iq:auth}query')
auth_request.attrib['to'] = self.xmpp.server auth_request.attrib['to'] = self.xmpp.server
@ -47,12 +50,12 @@ class xep_0078(base.base_plugin):
query.append(username) query.append(username)
query.append(resource) query.append(resource)
if rquery.find('{jabber:iq:auth}digest') is None: if rquery.find('{jabber:iq:auth}digest') is None:
logging.warning("Authenticating via jabber:iq:auth Plain.") log.warning("Authenticating via jabber:iq:auth Plain.")
password = ET.Element('password') password = ET.Element('password')
password.text = self.xmpp.password password.text = self.xmpp.password
query.append(password) query.append(password)
else: else:
logging.debug("Authenticating via jabber:iq:auth Digest") log.debug("Authenticating via jabber:iq:auth Digest")
digest = ET.Element('digest') digest = ET.Element('digest')
digest.text = hashlib.sha1(b"%s%s" % (self.streamid, self.xmpp.password)).hexdigest() digest.text = hashlib.sha1(b"%s%s" % (self.streamid, self.xmpp.password)).hexdigest()
query.append(digest) query.append(digest)
@ -64,6 +67,6 @@ class xep_0078(base.base_plugin):
self.xmpp.sessionstarted = True self.xmpp.sessionstarted = True
self.xmpp.event("session_start") self.xmpp.event("session_start")
else: else:
logging.info("Authentication failed") log.info("Authentication failed")
self.xmpp.disconnect() self.xmpp.disconnect()
self.xmpp.event("failed_auth") self.xmpp.event("failed_auth")

View file

@ -14,15 +14,18 @@ from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID
from .. stanza.message import Message from .. stanza.message import Message
log = logging.getLogger(__name__)
class ChatState(ElementBase): class ChatState(ElementBase):
namespace = 'http://jabber.org/protocol/chatstates' namespace = 'http://jabber.org/protocol/chatstates'
plugin_attrib = 'chat_state' plugin_attrib = 'chat_state'
interface = set(('state',)) interface = set(('state',))
states = set(('active', 'composing', 'gone', 'inactive', 'paused')) states = set(('active', 'composing', 'gone', 'inactive', 'paused'))
def active(self): def active(self):
self.setState('active') self.setState('active')
def composing(self): def composing(self):
self.setState('composing') self.setState('composing')
@ -67,11 +70,11 @@ class xep_0085(base.base_plugin):
""" """
XEP-0085 Chat State Notifications XEP-0085 Chat State Notifications
""" """
def plugin_init(self): def plugin_init(self):
self.xep = '0085' self.xep = '0085'
self.description = 'Chat State Notifications' self.description = 'Chat State Notifications'
handlers = [('Active Chat State', 'active'), handlers = [('Active Chat State', 'active'),
('Composing Chat State', 'composing'), ('Composing Chat State', 'composing'),
('Gone Chat State', 'gone'), ('Gone Chat State', 'gone'),
@ -79,10 +82,10 @@ class xep_0085(base.base_plugin):
('Paused Chat State', 'paused')] ('Paused Chat State', 'paused')]
for handler in handlers: for handler in handlers:
self.xmpp.registerHandler( self.xmpp.registerHandler(
Callback(handler[0], Callback(handler[0],
MatchXPath("{%s}message/{%s}%s" % (self.xmpp.default_ns, MatchXPath("{%s}message/{%s}%s" % (self.xmpp.default_ns,
ChatState.namespace, ChatState.namespace,
handler[1])), handler[1])),
self._handleChatState)) self._handleChatState))
registerStanzaPlugin(Message, Active) registerStanzaPlugin(Message, Active)
@ -90,12 +93,12 @@ class xep_0085(base.base_plugin):
registerStanzaPlugin(Message, Gone) registerStanzaPlugin(Message, Gone)
registerStanzaPlugin(Message, Inactive) registerStanzaPlugin(Message, Inactive)
registerStanzaPlugin(Message, Paused) registerStanzaPlugin(Message, Paused)
def post_init(self): def post_init(self):
base.base_plugin.post_init(self) base.base_plugin.post_init(self)
self.xmpp.plugin['xep_0030'].add_feature('http://jabber.org/protocol/chatstates') self.xmpp.plugin['xep_0030'].add_feature('http://jabber.org/protocol/chatstates')
def _handleChatState(self, msg): def _handleChatState(self, msg):
state = msg['chat_state'].name state = msg['chat_state'].name
logging.debug("Chat State: %s, %s" % (state, msg['from'].jid)) log.debug("Chat State: %s, %s" % (state, msg['from'].jid))
self.xmpp.event('chatstate_%s' % state, msg) self.xmpp.event('chatstate_%s' % state, msg)

View file

@ -2,7 +2,7 @@
SleekXMPP: The Sleek XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP. This file is part of SleekXMPP.
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
from xml.etree import cElementTree as ET from xml.etree import cElementTree as ET
@ -10,50 +10,54 @@ from . import base
import time import time
import logging import logging
log = logging.getLogger(__name__)
class xep_0199(base.base_plugin): class xep_0199(base.base_plugin):
"""XEP-0199 XMPP Ping""" """XEP-0199 XMPP Ping"""
def plugin_init(self): def plugin_init(self):
self.description = "XMPP Ping" self.description = "XMPP Ping"
self.xep = "0199" self.xep = "0199"
self.xmpp.add_handler("<iq type='get' xmlns='%s'><ping xmlns='http://www.xmpp.org/extensions/xep-0199.html#ns'/></iq>" % self.xmpp.default_ns, self.handler_ping, name='XMPP Ping') self.xmpp.add_handler("<iq type='get' xmlns='%s'><ping xmlns='urn:xmpp:ping'/></iq>" % self.xmpp.default_ns, self.handler_ping, name='XMPP Ping')
self.running = False if self.config.get('keepalive', True):
#if self.config.get('keepalive', True): self.xmpp.add_event_handler('session_start', self.handler_pingserver, threaded=True)
#self.xmpp.add_event_handler('session_start', self.handler_pingserver, threaded=True)
def post_init(self):
base.base_plugin.post_init(self)
self.xmpp.plugin['xep_0030'].add_feature('http://www.xmpp.org/extensions/xep-0199.html#ns')
def handler_pingserver(self, xml):
if not self.running:
time.sleep(self.config.get('frequency', 300))
while self.sendPing(self.xmpp.server, self.config.get('timeout', 30)) is not False:
time.sleep(self.config.get('frequency', 300))
logging.debug("Did not recieve ping back in time. Requesting Reconnect.")
self.xmpp.disconnect(reconnect=True)
def handler_ping(self, xml):
iq = self.xmpp.makeIqResult(xml.get('id', 'unknown'))
iq.attrib['to'] = xml.get('from', self.xmpp.server)
self.xmpp.send(iq)
def sendPing(self, jid, timeout = 30): def post_init(self):
""" sendPing(jid, timeout) base.base_plugin.post_init(self)
Sends a ping to the specified jid, returning the time (in seconds) self.xmpp.plugin['xep_0030'].add_feature('urn:xmpp:ping')
to receive a reply, or None if no reply is received in timeout seconds.
""" def handler_pingserver(self, xml):
id = self.xmpp.getNewId() self.xmpp.schedule("xep-0119 ping", float(self.config.get('frequency', 300)), self.scheduled_ping, repeat=True)
iq = self.xmpp.makeIq(id)
iq.attrib['type'] = 'get' def scheduled_ping(self):
iq.attrib['to'] = jid log.debug("pinging...")
ping = ET.Element('{http://www.xmpp.org/extensions/xep-0199.html#ns}ping') if self.sendPing(self.xmpp.server, self.config.get('timeout', 30)) is False:
iq.append(ping) log.debug("Did not recieve ping back in time. Requesting Reconnect.")
startTime = time.clock() self.xmpp.reconnect()
#pingresult = self.xmpp.send(iq, self.xmpp.makeIq(id), timeout)
pingresult = iq.send() def handler_ping(self, xml):
endTime = time.clock() iq = self.xmpp.makeIqResult(xml.get('id', 'unknown'))
if pingresult == False: iq.attrib['to'] = xml.get('from', self.xmpp.boundjid.domain)
#self.xmpp.disconnect(reconnect=True) self.xmpp.send(iq)
return False
return endTime - startTime def sendPing(self, jid, timeout = 30):
""" sendPing(jid, timeout)
Sends a ping to the specified jid, returning the time (in seconds)
to receive a reply, or None if no reply is received in timeout seconds.
"""
id = self.xmpp.getNewId()
iq = self.xmpp.makeIq(id)
iq.attrib['type'] = 'get'
iq.attrib['to'] = jid
ping = ET.Element('{urn:xmpp:ping}ping')
iq.append(ping)
startTime = time.clock()
#pingresult = self.xmpp.send(iq, self.xmpp.makeIq(id), timeout)
pingresult = iq.send()
endTime = time.clock()
if pingresult == False:
#self.xmpp.disconnect(reconnect=True)
return False
return endTime - startTime

View file

@ -17,6 +17,9 @@ from .. xmlstream.matcher.xpath import MatchXPath
from .. xmlstream import ElementBase, ET, JID, register_stanza_plugin from .. xmlstream import ElementBase, ET, JID, register_stanza_plugin
log = logging.getLogger(__name__)
class EntityTime(ElementBase): class EntityTime(ElementBase):
name = 'time' name = 'time'
namespace = 'urn:xmpp:time' namespace = 'urn:xmpp:time'
@ -84,10 +87,10 @@ class xep_0202(base.base_plugin):
def handle_entity_time_query(self, iq): def handle_entity_time_query(self, iq):
if iq['type'] == 'get': if iq['type'] == 'get':
logging.debug("Entity time requested by %s" % iq['from']) log.debug("Entity time requested by %s" % iq['from'])
self.xmpp.event('entity_time_request', iq) self.xmpp.event('entity_time_request', iq)
elif iq['type'] == 'result': elif iq['type'] == 'result':
logging.debug("Entity time result from %s" % iq['from']) log.debug("Entity time result from %s" % iq['from'])
self.xmpp.event('entity_time', iq) self.xmpp.event('entity_time', iq)
def handle_entity_time(self, iq): def handle_entity_time(self, iq):

View file

@ -442,7 +442,6 @@ class RosterItem(object):
key -- The state field to modify. key -- The state field to modify.
value -- The new value of the state field. value -- The new value of the state field.
""" """
print "%s: %s" % (key, value)
if key in self._state: if key in self._state:
if key in ['name', 'subscription', 'groups']: if key in ['name', 'subscription', 'groups']:
self._state[key] = value self._state[key] = value
@ -465,7 +464,7 @@ class RosterItem(object):
def remove(self): def remove(self):
""" """
Remove a JID's whitelisted status and unsubscribe if a Remove a JID's whitelisted status and unsubscribe if a
subscription exists. subscription exists.
""" """
if self['to']: if self['to']:

View file

@ -15,6 +15,9 @@ from sleekxmpp.stanza import Error
from sleekxmpp.xmlstream import ET, StanzaBase, register_stanza_plugin from sleekxmpp.xmlstream import ET, StanzaBase, register_stanza_plugin
log = logging.getLogger(__name__)
class RootStanza(StanzaBase): class RootStanza(StanzaBase):
""" """
@ -58,7 +61,7 @@ class RootStanza(StanzaBase):
self['error']['text'] = "SleekXMPP got into trouble." self['error']['text'] = "SleekXMPP got into trouble."
else: else:
self['error']['text'] = traceback.format_tb(e.__traceback__) self['error']['text'] = traceback.format_tb(e.__traceback__)
logging.exception('Error handling {%s}%s stanza' % log.exception('Error handling {%s}%s stanza' %
(self.namespace, self.name)) (self.namespace, self.name))
self.send() self.send()

View file

@ -1,5 +1,4 @@
""" """
SleekXMPP: The Sleek XMPP Library SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP. This file is part of SleekXMPP.
@ -27,27 +26,29 @@ class SleekTest(unittest.TestCase):
Message -- Create a Message stanza object. Message -- Create a Message stanza object.
Iq -- Create an Iq stanza object. Iq -- Create an Iq stanza object.
Presence -- Create a Presence stanza object. Presence -- Create a Presence stanza object.
check_stanza -- Compare a generic stanza against an XML string. check_jid -- Check a JID and its component parts.
check_message -- Compare a Message stanza against an XML string. check -- Compare a stanza against an XML string.
check_iq -- Compare an Iq stanza against an XML string.
check_presence -- Compare a Presence stanza against an XML string.
stream_start -- Initialize a dummy XMPP client. stream_start -- Initialize a dummy XMPP client.
stream_recv -- Queue data for XMPP client to receive.
stream_make_header -- Create a stream header.
stream_send_header -- Check that the given header has been sent.
stream_send_message -- Check that the XMPP client sent the given
Message stanza.
stream_send_iq -- Check that the XMPP client sent the given
Iq stanza.
stream_send_presence -- Check thatt the XMPP client sent the given
Presence stanza.
stream_send_stanza -- Check that the XMPP client sent the given
generic stanza.
stream_close -- Disconnect the XMPP client. stream_close -- Disconnect the XMPP client.
make_header -- Create a stream header.
send_header -- Check that the given header has been sent.
send_feature -- Send a raw XML element.
send -- Check that the XMPP client sent the given
generic stanza.
recv -- Queue data for XMPP client to receive, or
verify the data that was received from a
live connection.
recv_header -- Check that a given stream header
was received.
recv_feature -- Check that a given, raw XML element
was recveived.
fix_namespaces -- Add top-level namespace to an XML object. fix_namespaces -- Add top-level namespace to an XML object.
compare -- Compare XML objects against each other. compare -- Compare XML objects against each other.
""" """
def runTest(self):
pass
def parse_xml(self, xml_string): def parse_xml(self, xml_string):
try: try:
xml = ET.fromstring(xml_string) xml = ET.fromstring(xml_string)
@ -103,10 +104,8 @@ class SleekTest(unittest.TestCase):
""" """
return Presence(None, *args, **kwargs) return Presence(None, *args, **kwargs)
def check_jid(self, jid, user=None, domain=None, resource=None,
bare=None, full=None, string=None):
def check_JID(self, jid, user=None, domain=None, resource=None,
bare=None, full=None, string=None):
""" """
Verify the components of a JID. Verify the components of a JID.
@ -142,7 +141,6 @@ class SleekTest(unittest.TestCase):
afrom=None, ato=None, pending_out=None, pending_in=None, afrom=None, ato=None, pending_out=None, pending_in=None,
groups=None): groups=None):
roster = self.xmpp.roster[owner][jid] roster = self.xmpp.roster[owner][jid]
print roster._state
if name is not None: if name is not None:
self.assertEqual(roster['name'], name, self.assertEqual(roster['name'], name,
"Incorrect name value: %s" % roster['name']) "Incorrect name value: %s" % roster['name'])
@ -168,8 +166,8 @@ class SleekTest(unittest.TestCase):
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# Methods for comparing stanza objects to XML strings # Methods for comparing stanza objects to XML strings
def check_stanza(self, stanza_class, stanza, xml_string, def check(self, stanza, xml_string,
defaults=None, use_values=True): defaults=None, use_values=True):
""" """
Create and compare several stanza objects to a correct XML string. Create and compare several stanza objects to a correct XML string.
@ -188,7 +186,6 @@ class SleekTest(unittest.TestCase):
must take into account any extra elements that are included by default. must take into account any extra elements that are included by default.
Arguments: Arguments:
stanza_class -- The class of the stanza being tested.
stanza -- The stanza object to test. stanza -- The stanza object to test.
xml_string -- A string version of the correct XML expected. xml_string -- A string version of the correct XML expected.
defaults -- A list of stanza interfaces that have default defaults -- A list of stanza interfaces that have default
@ -199,6 +196,7 @@ class SleekTest(unittest.TestCase):
setStanzaValues() should be used. Defaults to setStanzaValues() should be used. Defaults to
True. True.
""" """
stanza_class = stanza.__class__
xml = self.parse_xml(xml_string) xml = self.parse_xml(xml_string)
# Ensure that top level namespaces are used, even if they # Ensure that top level namespaces are used, even if they
@ -215,7 +213,11 @@ class SleekTest(unittest.TestCase):
# so that they will compare correctly. # so that they will compare correctly.
default_stanza = stanza_class() default_stanza = stanza_class()
if defaults is None: if defaults is None:
defaults = [] known_defaults = {
Message: ['type'],
Presence: ['priority']
}
defaults = known_defaults.get(stanza_class, [])
for interface in defaults: for interface in defaults:
stanza[interface] = stanza[interface] stanza[interface] = stanza[interface]
stanza2[interface] = stanza2[interface] stanza2[interface] = stanza2[interface]
@ -246,62 +248,6 @@ class SleekTest(unittest.TestCase):
self.failUnless(result, debug) self.failUnless(result, debug)
def check_message(self, msg, xml_string, use_values=True):
"""
Create and compare several message stanza objects to a
correct XML string.
If use_values is False, the test using getStanzaValues() and
setStanzaValues() will not be used.
Arguments:
msg -- The Message stanza object to check.
xml_string -- The XML contents to compare against.
use_values -- Indicates if the test using getStanzaValues
and setStanzaValues should be used. Defaults
to True.
"""
return self.check_stanza(Message, msg, xml_string,
defaults=['type'],
use_values=use_values)
def check_iq(self, iq, xml_string, use_values=True):
"""
Create and compare several iq stanza objects to a
correct XML string.
If use_values is False, the test using getStanzaValues() and
setStanzaValues() will not be used.
Arguments:
iq -- The Iq stanza object to check.
xml_string -- The XML contents to compare against.
use_values -- Indicates if the test using getStanzaValues
and setStanzaValues should be used. Defaults
to True.
"""
return self.check_stanza(Iq, iq, xml_string, use_values=use_values)
def check_presence(self, pres, xml_string, use_values=True):
"""
Create and compare several presence stanza objects to a
correct XML string.
If use_values is False, the test using getStanzaValues() and
setStanzaValues() will not be used.
Arguments:
iq -- The Iq stanza object to check.
xml_string -- The XML contents to compare against.
use_values -- Indicates if the test using getStanzaValues
and setStanzaValues should be used. Defaults
to True.
"""
return self.check_stanza(Presence, pres, xml_string,
defaults=['priority'],
use_values=use_values)
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# Methods for simulating stanza streams. # Methods for simulating stanza streams.
@ -329,7 +275,6 @@ class SleekTest(unittest.TestCase):
port -- The port to use when connecting to the server. port -- The port to use when connecting to the server.
Defaults to 5222. Defaults to 5222.
""" """
if mode == 'client': if mode == 'client':
self.xmpp = ClientXMPP(jid, password) self.xmpp = ClientXMPP(jid, password)
elif mode == 'component': elif mode == 'component':
@ -364,13 +309,13 @@ class SleekTest(unittest.TestCase):
if mode == 'component': if mode == 'component':
self.xmpp.socket.next_sent(timeout=1) self.xmpp.socket.next_sent(timeout=1)
def stream_make_header(self, sto='', def make_header(self, sto='',
sfrom='', sfrom='',
sid='', sid='',
stream_ns="http://etherx.jabber.org/streams", stream_ns="http://etherx.jabber.org/streams",
default_ns="jabber:client", default_ns="jabber:client",
version="1.0", version="1.0",
xml_header=True): xml_header=True):
""" """
Create a stream header to be received by the test XMPP agent. Create a stream header to be received by the test XMPP agent.
@ -401,8 +346,8 @@ class SleekTest(unittest.TestCase):
parts.append('xmlns="%s"' % default_ns) parts.append('xmlns="%s"' % default_ns)
return header % ' '.join(parts) return header % ' '.join(parts)
def stream_recv(self, data, stanza_class=StanzaBase, defaults=[], def recv(self, data, stanza_class=StanzaBase, defaults=[],
use_values=True, timeout=1): use_values=True, timeout=1):
""" """
Pass data to the dummy XMPP client as if it came from an XMPP server. Pass data to the dummy XMPP client as if it came from an XMPP server.
@ -429,7 +374,7 @@ class SleekTest(unittest.TestCase):
if recv_data is None: if recv_data is None:
return False return False
stanza = stanza_class(xml=self.parse_xml(recv_data)) stanza = stanza_class(xml=self.parse_xml(recv_data))
return self.check_stanza(stanza_class, stanza, data, return self.check(stanza_class, stanza, data,
defaults=defaults, defaults=defaults,
use_values=use_values) use_values=use_values)
else: else:
@ -437,14 +382,14 @@ class SleekTest(unittest.TestCase):
data = str(data) data = str(data)
self.xmpp.socket.recv_data(data) self.xmpp.socket.recv_data(data)
def stream_recv_header(self, sto='', def recv_header(self, sto='',
sfrom='', sfrom='',
sid='', sid='',
stream_ns="http://etherx.jabber.org/streams", stream_ns="http://etherx.jabber.org/streams",
default_ns="jabber:client", default_ns="jabber:client",
version="1.0", version="1.0",
xml_header=False, xml_header=False,
timeout=1): timeout=1):
""" """
Check that a given stream header was received. Check that a given stream header was received.
@ -460,11 +405,11 @@ class SleekTest(unittest.TestCase):
timeout -- Length of time to wait in seconds for a timeout -- Length of time to wait in seconds for a
response. response.
""" """
header = self.stream_make_header(sto, sfrom, sid, header = self.make_header(sto, sfrom, sid,
stream_ns=stream_ns, stream_ns=stream_ns,
default_ns=default_ns, default_ns=default_ns,
version=version, version=version,
xml_header=xml_header) xml_header=xml_header)
recv_header = self.xmpp.socket.next_recv(timeout) recv_header = self.xmpp.socket.next_recv(timeout)
if recv_header is None: if recv_header is None:
raise ValueError("Socket did not return data.") raise ValueError("Socket did not return data.")
@ -504,9 +449,8 @@ class SleekTest(unittest.TestCase):
"Stream headers do not match:\nDesired:\n%s\nReceived:\n%s" % ( "Stream headers do not match:\nDesired:\n%s\nReceived:\n%s" % (
'%s %s' % (xml.tag, xml.attrib), '%s %s' % (xml.tag, xml.attrib),
'%s %s' % (recv_xml.tag, recv_xml.attrib))) '%s %s' % (recv_xml.tag, recv_xml.attrib)))
#tostring(xml), tostring(recv_xml)))#recv_header))
def stream_recv_feature(self, data, use_values=True, timeout=1): def recv_feature(self, data, use_values=True, timeout=1):
""" """
""" """
if self.xmpp.socket.is_live: if self.xmpp.socket.is_live:
@ -526,39 +470,14 @@ class SleekTest(unittest.TestCase):
data = str(data) data = str(data)
self.xmpp.socket.recv_data(data) self.xmpp.socket.recv_data(data)
def send_header(self, sto='',
sfrom='',
def stream_recv_message(self, data, use_values=True, timeout=1): sid='',
""" stream_ns="http://etherx.jabber.org/streams",
""" default_ns="jabber:client",
return self.stream_recv(data, stanza_class=Message, version="1.0",
defaults=['type'], xml_header=False,
use_values=use_values, timeout=1):
timeout=timeout)
def stream_recv_iq(self, data, use_values=True, timeout=1):
"""
"""
return self.stream_recv(data, stanza_class=Iq,
use_values=use_values,
timeout=timeout)
def stream_recv_presence(self, data, use_values=True, timeout=1):
"""
"""
return self.stream_recv(data, stanza_class=Presence,
defaults=['priority'],
use_values=use_values,
timeout=timeout)
def stream_send_header(self, sto='',
sfrom='',
sid='',
stream_ns="http://etherx.jabber.org/streams",
default_ns="jabber:client",
version="1.0",
xml_header=False,
timeout=1):
""" """
Check that a given stream header was sent. Check that a given stream header was sent.
@ -574,11 +493,11 @@ class SleekTest(unittest.TestCase):
timeout -- Length of time to wait in seconds for a timeout -- Length of time to wait in seconds for a
response. response.
""" """
header = self.stream_make_header(sto, sfrom, sid, header = self.make_header(sto, sfrom, sid,
stream_ns=stream_ns, stream_ns=stream_ns,
default_ns=default_ns, default_ns=default_ns,
version=version, version=version,
xml_header=xml_header) xml_header=xml_header)
sent_header = self.xmpp.socket.next_sent(timeout) sent_header = self.xmpp.socket.next_sent(timeout)
if sent_header is None: if sent_header is None:
raise ValueError("Socket did not return data.") raise ValueError("Socket did not return data.")
@ -596,7 +515,7 @@ class SleekTest(unittest.TestCase):
"Stream headers do not match:\nDesired:\n%s\nSent:\n%s" % ( "Stream headers do not match:\nDesired:\n%s\nSent:\n%s" % (
header, sent_header)) header, sent_header))
def stream_send_feature(self, data, use_values=True, timeout=1): def send_feature(self, data, use_values=True, timeout=1):
""" """
""" """
sent_data = self.xmpp.socket.next_sent(timeout) sent_data = self.xmpp.socket.next_sent(timeout)
@ -608,13 +527,13 @@ class SleekTest(unittest.TestCase):
"Features do not match.\nDesired:\n%s\nSent:\n%s" % ( "Features do not match.\nDesired:\n%s\nSent:\n%s" % (
tostring(xml), tostring(sent_xml))) tostring(xml), tostring(sent_xml)))
def stream_send_stanza(self, stanza_class, data, defaults=None, def send(self, data, defaults=None,
use_values=True, timeout=.1): use_values=True, timeout=.1):
""" """
Check that the XMPP client sent the given stanza XML. Check that the XMPP client sent the given stanza XML.
Extracts the next sent stanza and compares it with the given Extracts the next sent stanza and compares it with the given
XML using check_stanza. XML using check.
Arguments: Arguments:
stanza_class -- The class of the sent stanza object. stanza_class -- The class of the sent stanza object.
@ -626,70 +545,15 @@ class SleekTest(unittest.TestCase):
timeout -- Time in seconds to wait for a stanza before timeout -- Time in seconds to wait for a stanza before
failing the check. failing the check.
""" """
if isintance(data, str): if isinstance(data, str):
data = stanza_class(xml=self.parse_xml(data)) xml = self.parse_xml(data)
self.fix_namespaces(xml, 'jabber:client')
data = self.xmpp._build_stanza(xml, 'jabber:client')
sent = self.xmpp.socket.next_sent(timeout) sent = self.xmpp.socket.next_sent(timeout)
self.check_stanza(stanza_class, data, sent, self.check(data, sent,
defaults=defaults, defaults=defaults,
use_values=use_values) use_values=use_values)
def stream_send_message(self, data, use_values=True, timeout=.1):
"""
Check that the XMPP client sent the given stanza XML.
Extracts the next sent stanza and compares it with the given
XML using check_message.
Arguments:
data -- The XML string of the expected Message stanza,
or an equivalent stanza object.
use_values -- Modifies the type of tests used by check_message.
timeout -- Time in seconds to wait for a stanza before
failing the check.
"""
if isinstance(data, str):
data = self.Message(xml=self.parse_xml(data))
sent = self.xmpp.socket.next_sent(timeout)
self.check_message(data, sent, use_values)
def stream_send_iq(self, data, use_values=True, timeout=.1):
"""
Check that the XMPP client sent the given stanza XML.
Extracts the next sent stanza and compares it with the given
XML using check_iq.
Arguments:
data -- The XML string of the expected Iq stanza,
or an equivalent stanza object.
use_values -- Modifies the type of tests used by check_iq.
timeout -- Time in seconds to wait for a stanza before
failing the check.
"""
if isinstance(data, str):
data = self.Iq(xml=self.parse_xml(data))
sent = self.xmpp.socket.next_sent(timeout)
self.check_iq(data, sent, use_values)
def stream_send_presence(self, data, use_values=True, timeout=.1):
"""
Check that the XMPP client sent the given stanza XML.
Extracts the next sent stanza and compares it with the given
XML using check_presence.
Arguments:
data -- The XML string of the expected Presence stanza,
or an equivalent stanza object.
use_values -- Modifies the type of tests used by check_presence.
timeout -- Time in seconds to wait for a stanza before
failing the check.
"""
if isinstance(data, str):
data = self.Presence(xml=self.parse_xml(data))
sent = self.xmpp.socket.next_sent(timeout)
self.check_presence(data, sent, use_values)
def stream_close(self): def stream_close(self):
""" """
Disconnect the dummy XMPP client. Disconnect the dummy XMPP client.

View file

@ -21,7 +21,7 @@ class StateMachine(object):
self.addStates(states) self.addStates(states)
self.__default_state = self.__states[0] self.__default_state = self.__states[0]
self.__current_state = self.__default_state self.__current_state = self.__default_state
def addStates(self, states): def addStates(self, states):
self.lock.acquire() self.lock.acquire()
try: try:
@ -30,19 +30,19 @@ class StateMachine(object):
raise IndexError("The state '%s' is already in the StateMachine." % state) raise IndexError("The state '%s' is already in the StateMachine." % state)
self.__states.append(state) self.__states.append(state)
finally: self.lock.release() finally: self.lock.release()
def transition(self, from_state, to_state, wait=0.0, func=None, args=[], kwargs={}): def transition(self, from_state, to_state, wait=0.0, func=None, args=[], kwargs={}):
''' '''
Transition from the given `from_state` to the given `to_state`. Transition from the given `from_state` to the given `to_state`.
This method will return `True` if the state machine is now in `to_state`. It This method will return `True` if the state machine is now in `to_state`. It
will return `False` if a timeout occurred the transition did not occur. will return `False` if a timeout occurred the transition did not occur.
If `wait` is 0 (the default,) this method returns immediately if the state machine If `wait` is 0 (the default,) this method returns immediately if the state machine
is not in `from_state`. is not in `from_state`.
If you want the thread to block and transition once the state machine to enters If you want the thread to block and transition once the state machine to enters
`from_state`, set `wait` to a non-negative value. Note there is no 'block `from_state`, set `wait` to a non-negative value. Note there is no 'block
indefinitely' flag since this leads to deadlock. If you want to wait indefinitely, indefinitely' flag since this leads to deadlock. If you want to wait indefinitely,
choose a reasonable value for `wait` (e.g. 20 seconds) and do so in a while loop like so: choose a reasonable value for `wait` (e.g. 20 seconds) and do so in a while loop like so:
:: ::
@ -60,42 +60,42 @@ class StateMachine(object):
True value or if an exception is thrown, the transition will not occur. Any thrown True value or if an exception is thrown, the transition will not occur. Any thrown
exception is not caught by the state machine and is the caller's responsibility to handle. exception is not caught by the state machine and is the caller's responsibility to handle.
If `func` completes normally, this method will return the value returned by `func.` If If `func` completes normally, this method will return the value returned by `func.` If
values for `args` and `kwargs` are provided, they are expanded and passed like so: values for `args` and `kwargs` are provided, they are expanded and passed like so:
`func( *args, **kwargs )`. `func( *args, **kwargs )`.
''' '''
return self.transition_any((from_state,), to_state, wait=wait, return self.transition_any((from_state,), to_state, wait=wait,
func=func, args=args, kwargs=kwargs) func=func, args=args, kwargs=kwargs)
def transition_any(self, from_states, to_state, wait=0.0, func=None, args=[], kwargs={}): def transition_any(self, from_states, to_state, wait=0.0, func=None, args=[], kwargs={}):
''' '''
Transition from any of the given `from_states` to the given `to_state`. Transition from any of the given `from_states` to the given `to_state`.
''' '''
if not (isinstance(from_states,tuple) or isinstance(from_states,list)): if not (isinstance(from_states,tuple) or isinstance(from_states,list)):
raise ValueError("from_states should be a list or tuple") raise ValueError("from_states should be a list or tuple")
for state in from_states: for state in from_states:
if not state in self.__states: if not state in self.__states:
raise ValueError("StateMachine does not contain from_state %s." % state) raise ValueError("StateMachine does not contain from_state %s." % state)
if not to_state in self.__states: if not to_state in self.__states:
raise ValueError("StateMachine does not contain to_state %s." % to_state) raise ValueError("StateMachine does not contain to_state %s." % to_state)
start = time.time() start = time.time()
while not self.lock.acquire(False): while not self.lock.acquire(False):
time.sleep(.001) time.sleep(.001)
if (start + wait - time.time()) <= 0.0: if (start + wait - time.time()) <= 0.0:
logging.debug("Could not acquire lock") log.debug("Could not acquire lock")
return False return False
while not self.__current_state in from_states: while not self.__current_state in from_states:
# detect timeout: # detect timeout:
remainder = start + wait - time.time() remainder = start + wait - time.time()
if remainder > 0: if remainder > 0:
self.notifier.wait(remainder) self.notifier.wait(remainder)
else: else:
logging.debug("State was not ready") log.debug("State was not ready")
self.lock.release() self.lock.release()
return False return False
@ -105,9 +105,9 @@ class StateMachine(object):
# Note that func might throw an exception, but that's OK, it aborts the transition # Note that func might throw an exception, but that's OK, it aborts the transition
return_val = func(*args,**kwargs) if func is not None else True return_val = func(*args,**kwargs) if func is not None else True
# some 'false' value returned from func, # some 'false' value returned from func,
# indicating that transition should not occur: # indicating that transition should not occur:
if not return_val: return return_val if not return_val: return return_val
log.debug(' ==== TRANSITION %s -> %s', self.__current_state, to_state) log.debug(' ==== TRANSITION %s -> %s', self.__current_state, to_state)
self._set_state(to_state) self._set_state(to_state)
@ -115,7 +115,7 @@ class StateMachine(object):
else: else:
log.error("StateMachine bug!! The lock should ensure this doesn't happen!") log.error("StateMachine bug!! The lock should ensure this doesn't happen!")
return False return False
finally: finally:
self.notifier.set() # notify any waiting threads that the state has changed. self.notifier.set() # notify any waiting threads that the state has changed.
self.notifier.clear() self.notifier.clear()
self.lock.release() self.lock.release()
@ -125,13 +125,13 @@ class StateMachine(object):
''' '''
Use the state machine as a context manager. The transition occurs on /exit/ from Use the state machine as a context manager. The transition occurs on /exit/ from
the `with` context, so long as no exception is thrown. For example: the `with` context, so long as no exception is thrown. For example:
:: ::
with state_machine.transition_ctx('one','two', wait=5) as locked: with state_machine.transition_ctx('one','two', wait=5) as locked:
if locked: if locked:
# the state machine is currently locked in state 'one', and will # the state machine is currently locked in state 'one', and will
# transition to 'two' when the 'with' statement ends, so long as # transition to 'two' when the 'with' statement ends, so long as
# no exception is thrown. # no exception is thrown.
print 'Currently locked in state one: %s' % state_machine['one'] print 'Currently locked in state one: %s' % state_machine['one']
@ -142,20 +142,20 @@ class StateMachine(object):
print 'Since no exception was thrown, we are now in state "two": %s' % state_machine['two'] print 'Since no exception was thrown, we are now in state "two": %s' % state_machine['two']
The other main difference between this method and `transition()` is that the The other main difference between this method and `transition()` is that the
state machine is locked for the duration of the `with` statement. Normally, state machine is locked for the duration of the `with` statement. Normally,
after a `transition()` occurs, the state machine is immediately unlocked and after a `transition()` occurs, the state machine is immediately unlocked and
available to another thread to call `transition()` again. available to another thread to call `transition()` again.
''' '''
if not from_state in self.__states: if not from_state in self.__states:
raise ValueError("StateMachine does not contain from_state %s." % from_state) raise ValueError("StateMachine does not contain from_state %s." % from_state)
if not to_state in self.__states: if not to_state in self.__states:
raise ValueError("StateMachine does not contain to_state %s." % to_state) raise ValueError("StateMachine does not contain to_state %s." % to_state)
return _StateCtx(self, from_state, to_state, wait) return _StateCtx(self, from_state, to_state, wait)
def ensure(self, state, wait=0.0, block_on_transition=False): def ensure(self, state, wait=0.0, block_on_transition=False):
''' '''
Ensure the state machine is currently in `state`, or wait until it enters `state`. Ensure the state machine is currently in `state`, or wait until it enters `state`.
@ -168,24 +168,24 @@ class StateMachine(object):
Ensure we are currently in one of the given `states` or wait until Ensure we are currently in one of the given `states` or wait until
we enter one of those states. we enter one of those states.
Note that due to the nature of the function, you cannot guarantee that Note that due to the nature of the function, you cannot guarantee that
the entirety of some operation completes while you remain in a given the entirety of some operation completes while you remain in a given
state. That would require acquiring and holding a lock, which state. That would require acquiring and holding a lock, which
would mean no other threads could do the same. (You'd essentially would mean no other threads could do the same. (You'd essentially
be serializing all of the threads that are 'ensuring' their tasks be serializing all of the threads that are 'ensuring' their tasks
occurred in some state. occurred in some state.
''' '''
if not (isinstance(states,tuple) or isinstance(states,list)): if not (isinstance(states,tuple) or isinstance(states,list)):
raise ValueError('states arg should be a tuple or list') raise ValueError('states arg should be a tuple or list')
for state in states: for state in states:
if not state in self.__states: if not state in self.__states:
raise ValueError("StateMachine does not contain state '%s'" % state) raise ValueError("StateMachine does not contain state '%s'" % state)
# if we're in the middle of a transition, determine whether we should # if we're in the middle of a transition, determine whether we should
# 'fall back' to the 'current' state, or wait for the new state, in order to # 'fall back' to the 'current' state, or wait for the new state, in order to
# avoid an operation occurring in the wrong state. # avoid an operation occurring in the wrong state.
# TODO another option would be an ensure_ctx that uses a semaphore to allow # TODO another option would be an ensure_ctx that uses a semaphore to allow
# threads to indicate they want to remain in a particular state. # threads to indicate they want to remain in a particular state.
# will return immediately if no transition is in process. # will return immediately if no transition is in process.
@ -196,16 +196,16 @@ class StateMachine(object):
else: self.notifier.wait() else: self.notifier.wait()
start = time.time() start = time.time()
while not self.__current_state in states: while not self.__current_state in states:
# detect timeout: # detect timeout:
remainder = start + wait - time.time() remainder = start + wait - time.time()
if remainder > 0: self.notifier.wait(remainder) if remainder > 0: self.notifier.wait(remainder)
else: return False else: return False
return True return True
def reset(self): def reset(self):
# TODO need to lock before calling this? # TODO need to lock before calling this?
self.transition(self.__current_state, self.__default_state) self.transition(self.__current_state, self.__default_state)
@ -231,7 +231,7 @@ class StateMachine(object):
def __str__(self): def __str__(self):
return "".join(("StateMachine(", ','.join(self.__states), "): ", self.__current_state)) return "".join(("StateMachine(", ','.join(self.__states), "): ", self.__current_state))
class _StateCtx: class _StateCtx:
@ -244,28 +244,28 @@ class _StateCtx:
def __enter__(self): def __enter__(self):
start = time.time() start = time.time()
while not self.state_machine[self.from_state] or not self.state_machine.lock.acquire(False): while not self.state_machine[self.from_state] or not self.state_machine.lock.acquire(False):
# detect timeout: # detect timeout:
remainder = start + self.wait - time.time() remainder = start + self.wait - time.time()
if remainder > 0: self.state_machine.notifier.wait(remainder) if remainder > 0: self.state_machine.notifier.wait(remainder)
else: else:
log.debug('StateMachine timeout while waiting for state: %s', self.from_state) log.debug('StateMachine timeout while waiting for state: %s', self.from_state)
return False return False
self._locked = True # lock has been acquired at this point self._locked = True # lock has been acquired at this point
self.state_machine.notifier.clear() self.state_machine.notifier.clear()
log.debug('StateMachine entered context in state: %s', log.debug('StateMachine entered context in state: %s',
self.state_machine.current_state()) self.state_machine.current_state())
return True return True
def __exit__(self, exc_type, exc_val, exc_tb): def __exit__(self, exc_type, exc_val, exc_tb):
if exc_val is not None: if exc_val is not None:
log.exception("StateMachine exception in context, remaining in state: %s\n%s:%s", log.exception("StateMachine exception in context, remaining in state: %s\n%s:%s",
self.state_machine.current_state(), exc_type.__name__, exc_val) self.state_machine.current_state(), exc_type.__name__, exc_val)
if self._locked: if self._locked:
if exc_val is None: if exc_val is None:
log.debug(' ==== TRANSITION %s -> %s', log.debug(' ==== TRANSITION %s -> %s',
self.state_machine.current_state(), self.to_state) self.state_machine.current_state(), self.to_state)
self.state_machine._set_state(self.to_state) self.state_machine._set_state(self.to_state)

View file

@ -16,6 +16,9 @@ from sleekxmpp.xmlstream import StanzaBase, RESPONSE_TIMEOUT
from sleekxmpp.xmlstream.handler.base import BaseHandler from sleekxmpp.xmlstream.handler.base import BaseHandler
log = logging.getLogger(__name__)
class Waiter(BaseHandler): class Waiter(BaseHandler):
""" """
@ -85,7 +88,7 @@ class Waiter(BaseHandler):
stanza = self._payload.get(True, timeout) stanza = self._payload.get(True, timeout)
except queue.Empty: except queue.Empty:
stanza = False stanza = False
logging.warning("Timed out waiting for %s" % self.name) log.warning("Timed out waiting for %s" % self.name)
self.stream.removeHandler(self.name) self.stream.removeHandler(self.name)
return stanza return stanza

View file

@ -6,6 +6,8 @@
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
import logging
from xml.parsers.expat import ExpatError from xml.parsers.expat import ExpatError
from sleekxmpp.xmlstream.stanzabase import ET from sleekxmpp.xmlstream.stanzabase import ET
@ -18,6 +20,9 @@ from sleekxmpp.xmlstream.matcher.base import MatcherBase
IGNORE_NS = False IGNORE_NS = False
log = logging.getLogger(__name__)
class MatchXMLMask(MatcherBase): class MatchXMLMask(MatcherBase):
""" """
@ -97,8 +102,7 @@ class MatchXMLMask(MatcherBase):
try: try:
mask = ET.fromstring(mask) mask = ET.fromstring(mask)
except ExpatError: except ExpatError:
logging.log(logging.WARNING, log.warning("Expat error: %s\nIn parsing: %s" % ('', mask))
"Expat error: %s\nIn parsing: %s" % ('', mask))
if not use_ns: if not use_ns:
# Compare the element without using namespaces. # Compare the element without using namespaces.

View file

@ -15,6 +15,9 @@ except ImportError:
import Queue as queue import Queue as queue
log = logging.getLogger(__name__)
class Task(object): class Task(object):
""" """
@ -146,6 +149,8 @@ class Scheduler(object):
if wait <= 0.0: if wait <= 0.0:
newtask = self.addq.get(False) newtask = self.addq.get(False)
else: else:
if wait >= 3.0:
wait = 3.0
newtask = self.addq.get(True, wait) newtask = self.addq.get(True, wait)
except queue.Empty: except queue.Empty:
cleanup = [] cleanup = []
@ -168,13 +173,13 @@ class Scheduler(object):
except KeyboardInterrupt: except KeyboardInterrupt:
self.run = False self.run = False
if self.parentstop is not None: if self.parentstop is not None:
logging.debug("stopping parent") log.debug("stopping parent")
self.parentstop.set() self.parentstop.set()
except SystemExit: except SystemExit:
self.run = False self.run = False
if self.parentstop is not None: if self.parentstop is not None:
self.parentstop.set() self.parentstop.set()
logging.debug("Quitting Scheduler thread") log.debug("Quitting Scheduler thread")
if self.parentqueue is not None: if self.parentqueue is not None:
self.parentqueue.put(('quit', None, None)) self.parentqueue.put(('quit', None, None))

View file

@ -16,6 +16,9 @@ from sleekxmpp.xmlstream import JID
from sleekxmpp.xmlstream.tostring import tostring from sleekxmpp.xmlstream.tostring import tostring
log = logging.getLogger(__name__)
# Used to check if an argument is an XML object. # Used to check if an argument is an XML object.
XML_TYPE = type(ET.Element('xml')) XML_TYPE = type(ET.Element('xml'))
@ -1140,7 +1143,7 @@ class StanzaBase(ElementBase):
Meant to be overridden. Meant to be overridden.
""" """
logging.exception('Error handling {%s}%s stanza' % (self.namespace, log.exception('Error handling {%s}%s stanza' % (self.namespace,
self.name)) self.name))
def send(self): def send(self):

View file

@ -44,6 +44,9 @@ HANDLER_THREADS = 1
SSL_SUPPORT = True SSL_SUPPORT = True
log = logging.getLogger(__name__)
class RestartStream(Exception): class RestartStream(Exception):
""" """
Exception to restart stream processing, including Exception to restart stream processing, including
@ -87,6 +90,8 @@ class XMLStream(object):
send_queue -- A queue of stanzas to be sent on the stream. send_queue -- A queue of stanzas to be sent on the stream.
socket -- The connection to the server. socket -- The connection to the server.
ssl_support -- Indicates if a SSL library is available for use. ssl_support -- Indicates if a SSL library is available for use.
ssl_version -- The version of the SSL protocol to use.
Defaults to ssl.PROTOCOL_TLSv1.
state -- A state machine for managing the stream's state -- A state machine for managing the stream's
connection state. connection state.
stream_footer -- The start tag and any attributes for the stream's stream_footer -- The start tag and any attributes for the stream's
@ -155,6 +160,7 @@ class XMLStream(object):
self.sendXML = self.send_xml self.sendXML = self.send_xml
self.ssl_support = SSL_SUPPORT self.ssl_support = SSL_SUPPORT
self.ssl_version = ssl.PROTOCOL_TLSv1
self.state = StateMachine(('disconnected', 'connected')) self.state = StateMachine(('disconnected', 'connected'))
self.state._set_state('disconnected') self.state._set_state('disconnected')
@ -196,8 +202,15 @@ class XMLStream(object):
self.auto_reconnect = True self.auto_reconnect = True
self.is_client = False self.is_client = False
signal.signal(signal.SIGHUP, self._handle_kill) try:
signal.signal(signal.SIGTERM, self._handle_kill) # used in Windows if hasattr(signal, 'SIGHUP'):
signal.signal(signal.SIGHUP, self._handle_kill)
if hasattr(signal, 'SIGTERM'):
# Used in Windows
signal.signal(signal.SIGTERM, self._handle_kill)
except:
log.debug("Can not set interrupt signal handlers. " + \
"SleekXMPP is not running from a main thread.")
def _handle_kill(self, signum, frame): def _handle_kill(self, signum, frame):
""" """
@ -265,7 +278,7 @@ class XMLStream(object):
self.socket = self.socket_class(Socket.AF_INET, Socket.SOCK_STREAM) self.socket = self.socket_class(Socket.AF_INET, Socket.SOCK_STREAM)
self.socket.settimeout(None) self.socket.settimeout(None)
if self.use_ssl and self.ssl_support: if self.use_ssl and self.ssl_support:
logging.debug("Socket Wrapped for SSL") log.debug("Socket Wrapped for SSL")
ssl_socket = ssl.wrap_socket(self.socket) ssl_socket = ssl.wrap_socket(self.socket)
if hasattr(self.socket, 'socket'): if hasattr(self.socket, 'socket'):
# We are using a testing socket, so preserve the top # We are using a testing socket, so preserve the top
@ -275,7 +288,7 @@ class XMLStream(object):
self.socket = ssl_socket self.socket = ssl_socket
try: try:
logging.debug("Connecting to %s:%s" % self.address) log.debug("Connecting to %s:%s" % self.address)
self.socket.connect(self.address) self.socket.connect(self.address)
self.set_socket(self.socket, ignore=True) self.set_socket(self.socket, ignore=True)
#this event is where you should set your application state #this event is where you should set your application state
@ -283,7 +296,7 @@ class XMLStream(object):
return True return True
except Socket.error as serr: except Socket.error as serr:
error_msg = "Could not connect to %s:%s. Socket Error #%s: %s" error_msg = "Could not connect to %s:%s. Socket Error #%s: %s"
logging.error(error_msg % (self.address[0], self.address[1], log.error(error_msg % (self.address[0], self.address[1],
serr.errno, serr.strerror)) serr.errno, serr.strerror))
time.sleep(1) time.sleep(1)
return False return False
@ -328,10 +341,10 @@ class XMLStream(object):
""" """
Reset the stream's state and reconnect to the server. Reset the stream's state and reconnect to the server.
""" """
logging.debug("reconnecting...") log.debug("reconnecting...")
self.state.transition('connected', 'disconnected', wait=2.0, self.state.transition('connected', 'disconnected', wait=2.0,
func=self._disconnect, args=(True,)) func=self._disconnect, args=(True,))
logging.debug("connecting...") log.debug("connecting...")
return self.state.transition('disconnected', 'connected', return self.state.transition('disconnected', 'connected',
wait=2.0, func=self._connect) wait=2.0, func=self._connect)
@ -368,9 +381,10 @@ class XMLStream(object):
to be restarted. to be restarted.
""" """
if self.ssl_support: if self.ssl_support:
logging.info("Negotiating TLS") log.info("Negotiating TLS")
log.info("Using SSL version: %s" % str(self.ssl_version))
ssl_socket = ssl.wrap_socket(self.socket, ssl_socket = ssl.wrap_socket(self.socket,
ssl_version=ssl.PROTOCOL_TLSv1, ssl_version=self.ssl_version,
do_handshake_on_connect=False) do_handshake_on_connect=False)
if hasattr(self.socket, 'socket'): if hasattr(self.socket, 'socket'):
# We are using a testing socket, so preserve the top # We are using a testing socket, so preserve the top
@ -382,7 +396,7 @@ class XMLStream(object):
self.set_socket(self.socket) self.set_socket(self.socket)
return True return True
else: else:
logging.warning("Tried to enable TLS, but ssl module not found.") log.warning("Tried to enable TLS, but ssl module not found.")
return False return False
def start_stream_handler(self, xml): def start_stream_handler(self, xml):
@ -517,6 +531,17 @@ class XMLStream(object):
self.__event_handlers[name] = filter(filter_pointers, self.__event_handlers[name] = filter(filter_pointers,
self.__event_handlers[name]) self.__event_handlers[name])
def event_handled(self, name):
"""
Indicates if an event has any associated handlers.
Returns the number of registered handlers.
Arguments:
name -- The name of the event to check.
"""
return len(self.__event_handlers.get(name, []))
def event(self, name, data={}, direct=False): def event(self, name, data={}, direct=False):
""" """
Manually trigger a custom event. Manually trigger a custom event.
@ -525,13 +550,22 @@ class XMLStream(object):
name -- The name of the event to trigger. name -- The name of the event to trigger.
data -- Data that will be passed to each event handler. data -- Data that will be passed to each event handler.
Defaults to an empty dictionary. Defaults to an empty dictionary.
direct -- Runs the event directly if True. direct -- Runs the event directly if True, skipping the
event queue. All event handlers will run in the
same thread.
""" """
for handler in self.__event_handlers.get(name, []): for handler in self.__event_handlers.get(name, []):
if direct: if direct:
handler[0](copy.copy(data)) try:
handler[0](copy.copy(data))
except Exception as e:
error_msg = 'Error processing event handler: %s'
log.exception(error_msg % str(handler[0]))
if hasattr(data, 'exception'):
data.exception(e)
else: else:
self.event_queue.put(('event', handler, copy.copy(data))) self.event_queue.put(('event', handler, copy.copy(data)))
if handler[2]: if handler[2]:
# If the handler is disposable, we will go ahead and # If the handler is disposable, we will go ahead and
# remove it now instead of waiting for it to be # remove it now instead of waiting for it to be
@ -591,7 +625,7 @@ class XMLStream(object):
mask = mask.xml mask = mask.xml
data = str(data) data = str(data)
if mask is not None: if mask is not None:
logging.warning("Use of send mask waiters is deprecated.") log.warning("Use of send mask waiters is deprecated.")
wait_for = Waiter("SendWait_%s" % self.new_id(), wait_for = Waiter("SendWait_%s" % self.new_id(),
MatchXMLMask(mask)) MatchXMLMask(mask))
self.register_handler(wait_for) self.register_handler(wait_for)
@ -648,7 +682,7 @@ class XMLStream(object):
self.__thread[name].start() self.__thread[name].start()
for t in range(0, HANDLER_THREADS): for t in range(0, HANDLER_THREADS):
logging.debug("Starting HANDLER THREAD") log.debug("Starting HANDLER THREAD")
start_thread('stream_event_handler_%s' % t, self._event_runner) start_thread('stream_event_handler_%s' % t, self._event_runner)
start_thread('send_thread', self._send_thread) start_thread('send_thread', self._send_thread)
@ -686,16 +720,16 @@ class XMLStream(object):
if self.is_client: if self.is_client:
self.send_raw(self.stream_header) self.send_raw(self.stream_header)
except KeyboardInterrupt: except KeyboardInterrupt:
logging.debug("Keyboard Escape Detected in _process") log.debug("Keyboard Escape Detected in _process")
self.stop.set() self.stop.set()
except SystemExit: except SystemExit:
logging.debug("SystemExit in _process") log.debug("SystemExit in _process")
self.stop.set() self.stop.set()
except Socket.error: except Socket.error:
logging.exception('Socket Error') log.exception('Socket Error')
except: except:
if not self.stop.isSet(): if not self.stop.isSet():
logging.exception('Connection error.') log.exception('Connection error.')
if not self.stop.isSet() and self.auto_reconnect: if not self.stop.isSet() and self.auto_reconnect:
self.reconnect() self.reconnect()
else: else:
@ -725,7 +759,7 @@ class XMLStream(object):
if depth == 0: if depth == 0:
# The stream's root element has closed, # The stream's root element has closed,
# terminating the stream. # terminating the stream.
logging.debug("End of stream recieved") log.debug("End of stream recieved")
self.stream_end_event.set() self.stream_end_event.set()
return False return False
elif depth == 1: elif depth == 1:
@ -739,7 +773,29 @@ class XMLStream(object):
# Keep the root element empty of children to # Keep the root element empty of children to
# save on memory use. # save on memory use.
root.clear() root.clear()
logging.debug("Ending read XML loop") log.debug("Ending read XML loop")
def _build_stanza(self, xml, default_ns=None):
"""
Create a stanza object from a given XML object.
If a specialized stanza type is not found for the XML, then
a generic StanzaBase stanza will be returned.
Arguments:
xml -- The XML object to convert into a stanza object.
default_ns -- Optional default namespace to use instead of the
stream's current default namespace.
"""
if default_ns is None:
default_ns = self.default_ns
stanza_type = StanzaBase
for stanza_class in self.__root_stanza:
if xml.tag == "{%s}%s" % (default_ns, stanza_class.name):
stanza_type = stanza_class
break
stanza = stanza_type(self, xml)
return stanza
def __spawn_event(self, xml): def __spawn_event(self, xml):
""" """
@ -750,7 +806,7 @@ class XMLStream(object):
Arguments: Arguments:
xml -- The XML stanza to analyze. xml -- The XML stanza to analyze.
""" """
logging.debug("RECV: %s" % tostring(xml, log.debug("RECV: %s" % tostring(xml,
xmlns=self.default_ns, xmlns=self.default_ns,
stream=self)) stream=self))
# Apply any preprocessing filters. # Apply any preprocessing filters.
@ -788,7 +844,7 @@ class XMLStream(object):
def _threaded_event_wrapper(self, func, args): def _threaded_event_wrapper(self, func, args):
""" """
Capture exceptions for event handlers that run Capture exceptions for event handlers that run
in individual threads. in individual threads.
Arguments: Arguments:
@ -799,7 +855,7 @@ class XMLStream(object):
func(*args) func(*args)
except Exception as e: except Exception as e:
error_msg = 'Error processing event handler: %s' error_msg = 'Error processing event handler: %s'
logging.exception(error_msg % str(func)) log.exception(error_msg % str(func))
if hasattr(args[0], 'exception'): if hasattr(args[0], 'exception'):
args[0].exception(e) args[0].exception(e)
@ -812,7 +868,7 @@ class XMLStream(object):
Stream event handlers will all execute in this thread. Custom event Stream event handlers will all execute in this thread. Custom event
handlers may be spawned in individual threads. handlers may be spawned in individual threads.
""" """
logging.debug("Loading event runner") log.debug("Loading event runner")
try: try:
while not self.stop.isSet(): while not self.stop.isSet():
try: try:
@ -830,14 +886,14 @@ class XMLStream(object):
handler.run(args[0]) handler.run(args[0])
except Exception as e: except Exception as e:
error_msg = 'Error processing stream handler: %s' error_msg = 'Error processing stream handler: %s'
logging.exception(error_msg % handler.name) log.exception(error_msg % handler.name)
args[0].exception(e) args[0].exception(e)
elif etype == 'schedule': elif etype == 'schedule':
try: try:
logging.debug(args) log.debug(args)
handler(*args[0]) handler(*args[0])
except: except:
logging.exception('Error processing scheduled task') log.exception('Error processing scheduled task')
elif etype == 'event': elif etype == 'event':
func, threaded, disposable = handler func, threaded, disposable = handler
try: try:
@ -851,14 +907,14 @@ class XMLStream(object):
func(*args) func(*args)
except Exception as e: except Exception as e:
error_msg = 'Error processing event handler: %s' error_msg = 'Error processing event handler: %s'
logging.exception(error_msg % str(func)) log.exception(error_msg % str(func))
if hasattr(args[0], 'exception'): if hasattr(args[0], 'exception'):
args[0].exception(e) args[0].exception(e)
elif etype == 'quit': elif etype == 'quit':
logging.debug("Quitting event runner thread") log.debug("Quitting event runner thread")
return False return False
except KeyboardInterrupt: except KeyboardInterrupt:
logging.debug("Keyboard Escape Detected in _event_runner") log.debug("Keyboard Escape Detected in _event_runner")
self.disconnect() self.disconnect()
return return
except SystemExit: except SystemExit:
@ -876,14 +932,14 @@ class XMLStream(object):
data = self.send_queue.get(True, 1) data = self.send_queue.get(True, 1)
except queue.Empty: except queue.Empty:
continue continue
logging.debug("SEND: %s" % data) log.debug("SEND: %s" % data)
try: try:
self.socket.send(data.encode('utf-8')) self.socket.send(data.encode('utf-8'))
except: except:
logging.warning("Failed to send %s" % data) log.warning("Failed to send %s" % data)
self.disconnect(self.auto_reconnect) self.disconnect(self.auto_reconnect)
except KeyboardInterrupt: except KeyboardInterrupt:
logging.debug("Keyboard Escape Detected in _send_thread") log.debug("Keyboard Escape Detected in _send_thread")
self.disconnect() self.disconnect()
return return
except SystemExit: except SystemExit:

BIN
tests/__init__$py.class Normal file

Binary file not shown.

View file

@ -20,9 +20,9 @@ class TestLiveStream(SleekTest):
# Use sid=None to ignore any id sent by the server since # Use sid=None to ignore any id sent by the server since
# we can't know it in advance. # we can't know it in advance.
self.stream_recv_header(sfrom='localhost', sid=None) self.recv_header(sfrom='localhost', sid=None)
self.stream_send_header(sto='localhost') self.send_header(sto='localhost')
self.stream_recv_feature(""" self.recv_feature("""
<stream:features> <stream:features>
<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls" /> <starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls" />
<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"> <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
@ -35,15 +35,15 @@ class TestLiveStream(SleekTest):
<register xmlns="http://jabber.org/features/iq-register" /> <register xmlns="http://jabber.org/features/iq-register" />
</stream:features> </stream:features>
""") """)
self.stream_send_feature(""" self.send_feature("""
<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls" /> <starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls" />
""") """)
self.stream_recv_feature(""" self.recv_feature("""
<proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls" /> <proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls" />
""") """)
self.stream_send_header(sto='localhost') self.send_header(sto='localhost')
self.stream_recv_header(sfrom='localhost', sid=None) self.recv_header(sfrom='localhost', sid=None)
self.stream_recv_feature(""" self.recv_feature("""
<stream:features> <stream:features>
<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"> <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
<mechanism>DIGEST-MD5</mechanism> <mechanism>DIGEST-MD5</mechanism>
@ -56,16 +56,16 @@ class TestLiveStream(SleekTest):
<register xmlns="http://jabber.org/features/iq-register" /> <register xmlns="http://jabber.org/features/iq-register" />
</stream:features> </stream:features>
""") """)
self.stream_send_feature(""" self.send_feature("""
<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl"
mechanism="PLAIN">AHVzZXIAdXNlcg==</auth> mechanism="PLAIN">AHVzZXIAdXNlcg==</auth>
""") """)
self.stream_recv_feature(""" self.recv_feature("""
<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl" /> <success xmlns="urn:ietf:params:xml:ns:xmpp-sasl" />
""") """)
self.stream_send_header(sto='localhost') self.send_header(sto='localhost')
self.stream_recv_header(sfrom='localhost', sid=None) self.recv_header(sfrom='localhost', sid=None)
self.stream_recv_feature(""" self.recv_feature("""
<stream:features> <stream:features>
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind" /> <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind" />
<session xmlns="urn:ietf:params:xml:ns:xmpp-session" /> <session xmlns="urn:ietf:params:xml:ns:xmpp-session" />
@ -77,16 +77,16 @@ class TestLiveStream(SleekTest):
</stream:features> </stream:features>
""") """)
# Should really use stream_send_iq, but our Iq stanza objects # Should really use send, but our Iq stanza objects
# can't handle bind element payloads yet. # can't handle bind element payloads yet.
self.stream_send_feature(""" self.send_feature("""
<iq type="set" id="1"> <iq type="set" id="1">
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"> <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
<resource>test</resource> <resource>test</resource>
</bind> </bind>
</iq> </iq>
""") """)
self.stream_recv_feature(""" self.recv_feature("""
<iq type="result" id="1"> <iq type="result" id="1">
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"> <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
<jid>user@localhost/test</jid> <jid>user@localhost/test</jid>

View file

@ -8,7 +8,7 @@ class TestJIDClass(SleekTest):
def testJIDFromFull(self): def testJIDFromFull(self):
"""Test using JID of the form 'user@server/resource/with/slashes'.""" """Test using JID of the form 'user@server/resource/with/slashes'."""
self.check_JID(JID('user@someserver/some/resource'), self.check_jid(JID('user@someserver/some/resource'),
'user', 'user',
'someserver', 'someserver',
'some/resource', 'some/resource',
@ -22,7 +22,7 @@ class TestJIDClass(SleekTest):
j.user = 'user' j.user = 'user'
j.domain = 'someserver' j.domain = 'someserver'
j.resource = 'some/resource' j.resource = 'some/resource'
self.check_JID(j, self.check_jid(j,
'user', 'user',
'someserver', 'someserver',
'some/resource', 'some/resource',
@ -34,15 +34,15 @@ class TestJIDClass(SleekTest):
"""Test changing JID using aliases for domain.""" """Test changing JID using aliases for domain."""
j = JID('user@someserver/resource') j = JID('user@someserver/resource')
j.server = 'anotherserver' j.server = 'anotherserver'
self.check_JID(j, domain='anotherserver') self.check_jid(j, domain='anotherserver')
j.host = 'yetanother' j.host = 'yetanother'
self.check_JID(j, domain='yetanother') self.check_jid(j, domain='yetanother')
def testJIDSetFullWithUser(self): def testJIDSetFullWithUser(self):
"""Test setting the full JID with a user portion.""" """Test setting the full JID with a user portion."""
j = JID('user@domain/resource') j = JID('user@domain/resource')
j.full = 'otheruser@otherdomain/otherresource' j.full = 'otheruser@otherdomain/otherresource'
self.check_JID(j, self.check_jid(j,
'otheruser', 'otheruser',
'otherdomain', 'otherdomain',
'otherresource', 'otherresource',
@ -57,7 +57,7 @@ class TestJIDClass(SleekTest):
""" """
j = JID('user@domain/resource') j = JID('user@domain/resource')
j.full = 'otherdomain/otherresource' j.full = 'otherdomain/otherresource'
self.check_JID(j, self.check_jid(j,
'', '',
'otherdomain', 'otherdomain',
'otherresource', 'otherresource',
@ -72,7 +72,7 @@ class TestJIDClass(SleekTest):
""" """
j = JID('user@domain/resource') j = JID('user@domain/resource')
j.full = 'otherdomain' j.full = 'otherdomain'
self.check_JID(j, self.check_jid(j,
'', '',
'otherdomain', 'otherdomain',
'', '',
@ -84,7 +84,7 @@ class TestJIDClass(SleekTest):
"""Test setting the bare JID with a user.""" """Test setting the bare JID with a user."""
j = JID('user@domain/resource') j = JID('user@domain/resource')
j.bare = 'otheruser@otherdomain' j.bare = 'otheruser@otherdomain'
self.check_JID(j, self.check_jid(j,
'otheruser', 'otheruser',
'otherdomain', 'otherdomain',
'resource', 'resource',
@ -96,7 +96,7 @@ class TestJIDClass(SleekTest):
"""Test setting the bare JID without a user.""" """Test setting the bare JID without a user."""
j = JID('user@domain/resource') j = JID('user@domain/resource')
j.bare = 'otherdomain' j.bare = 'otherdomain'
self.check_JID(j, self.check_jid(j,
'', '',
'otherdomain', 'otherdomain',
'resource', 'resource',
@ -106,7 +106,7 @@ class TestJIDClass(SleekTest):
def testJIDNoResource(self): def testJIDNoResource(self):
"""Test using JID of the form 'user@domain'.""" """Test using JID of the form 'user@domain'."""
self.check_JID(JID('user@someserver'), self.check_jid(JID('user@someserver'),
'user', 'user',
'someserver', 'someserver',
'', '',
@ -116,7 +116,7 @@ class TestJIDClass(SleekTest):
def testJIDNoUser(self): def testJIDNoUser(self):
"""Test JID of the form 'component.domain.tld'.""" """Test JID of the form 'component.domain.tld'."""
self.check_JID(JID('component.someserver'), self.check_jid(JID('component.someserver'),
'', '',
'component.someserver', 'component.someserver',
'', '',

View file

@ -27,7 +27,7 @@ class TestElementBase(SleekTest):
namespace = "test" namespace = "test"
stanza = TestStanza() stanza = TestStanza()
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="test"> <foo xmlns="test">
<bar> <bar>
<baz /> <baz />
@ -117,7 +117,7 @@ class TestElementBase(SleekTest):
'baz': ''}]} 'baz': ''}]}
stanza.setStanzaValues(values) stanza.setStanzaValues(values)
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo" bar="a"> <foo xmlns="foo" bar="a">
<pluginfoo baz="b" /> <pluginfoo baz="b" />
<pluginfoo2 bar="d" baz="e" /> <pluginfoo2 bar="d" baz="e" />
@ -198,7 +198,7 @@ class TestElementBase(SleekTest):
stanza['qux'] = 'overridden' stanza['qux'] = 'overridden'
stanza['foobar'] = 'plugin' stanza['foobar'] = 'plugin'
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo" bar="attribute!"> <foo xmlns="foo" bar="attribute!">
<baz>element!</baz> <baz>element!</baz>
<foobar foobar="plugin" /> <foobar foobar="plugin" />
@ -231,7 +231,7 @@ class TestElementBase(SleekTest):
stanza['qux'] = 'c' stanza['qux'] = 'c'
stanza['foobar']['foobar'] = 'd' stanza['foobar']['foobar'] = 'd'
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo" baz="b" qux="c"> <foo xmlns="foo" baz="b" qux="c">
<bar>a</bar> <bar>a</bar>
<foobar foobar="d" /> <foobar foobar="d" />
@ -243,7 +243,7 @@ class TestElementBase(SleekTest):
del stanza['qux'] del stanza['qux']
del stanza['foobar'] del stanza['foobar']
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo" qux="c" /> <foo xmlns="foo" qux="c" />
""") """)
@ -257,7 +257,7 @@ class TestElementBase(SleekTest):
stanza = TestStanza() stanza = TestStanza()
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo" /> <foo xmlns="foo" />
""") """)
@ -267,7 +267,7 @@ class TestElementBase(SleekTest):
stanza._set_attr('bar', 'a') stanza._set_attr('bar', 'a')
stanza._set_attr('baz', 'b') stanza._set_attr('baz', 'b')
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo" bar="a" baz="b" /> <foo xmlns="foo" bar="a" baz="b" />
""") """)
@ -277,7 +277,7 @@ class TestElementBase(SleekTest):
stanza._set_attr('bar', None) stanza._set_attr('bar', None)
stanza._del_attr('baz') stanza._del_attr('baz')
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo" /> <foo xmlns="foo" />
""") """)
@ -307,7 +307,7 @@ class TestElementBase(SleekTest):
"Default _get_sub_text value incorrect.") "Default _get_sub_text value incorrect.")
stanza['bar'] = 'found' stanza['bar'] = 'found'
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo"> <foo xmlns="foo">
<wrapper> <wrapper>
<bar>found</bar> <bar>found</bar>
@ -340,7 +340,7 @@ class TestElementBase(SleekTest):
stanza = TestStanza() stanza = TestStanza()
stanza['bar'] = 'a' stanza['bar'] = 'a'
stanza['baz'] = 'b' stanza['baz'] = 'b'
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo"> <foo xmlns="foo">
<wrapper> <wrapper>
<bar>a</bar> <bar>a</bar>
@ -349,7 +349,7 @@ class TestElementBase(SleekTest):
</foo> </foo>
""") """)
stanza._set_sub_text('wrapper/bar', text='', keep=True) stanza._set_sub_text('wrapper/bar', text='', keep=True)
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo"> <foo xmlns="foo">
<wrapper> <wrapper>
<bar /> <bar />
@ -360,7 +360,7 @@ class TestElementBase(SleekTest):
stanza['bar'] = 'a' stanza['bar'] = 'a'
stanza._set_sub_text('wrapper/bar', text='') stanza._set_sub_text('wrapper/bar', text='')
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo"> <foo xmlns="foo">
<wrapper> <wrapper>
<baz>b</baz> <baz>b</baz>
@ -398,7 +398,7 @@ class TestElementBase(SleekTest):
stanza['bar'] = 'a' stanza['bar'] = 'a'
stanza['baz'] = 'b' stanza['baz'] = 'b'
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo"> <foo xmlns="foo">
<path> <path>
<to> <to>
@ -416,7 +416,7 @@ class TestElementBase(SleekTest):
del stanza['bar'] del stanza['bar']
del stanza['baz'] del stanza['baz']
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo"> <foo xmlns="foo">
<path> <path>
<to> <to>
@ -432,7 +432,7 @@ class TestElementBase(SleekTest):
stanza._del_sub('path/to/only/bar', all=True) stanza._del_sub('path/to/only/bar', all=True)
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo"> <foo xmlns="foo">
<path> <path>
<to> <to>
@ -603,7 +603,7 @@ class TestElementBase(SleekTest):
"Incorrect empty stanza size.") "Incorrect empty stanza size.")
stanza.append(substanza1) stanza.append(substanza1)
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo"> <foo xmlns="foo">
<foobar qux="a" /> <foobar qux="a" />
</foo> </foo>
@ -612,7 +612,7 @@ class TestElementBase(SleekTest):
"Incorrect stanza size with 1 substanza.") "Incorrect stanza size with 1 substanza.")
stanza.append(substanza2) stanza.append(substanza2)
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo"> <foo xmlns="foo">
<foobar qux="a" /> <foobar qux="a" />
<foobar qux="b" /> <foobar qux="b" />
@ -623,7 +623,7 @@ class TestElementBase(SleekTest):
# Test popping substanzas # Test popping substanzas
stanza.pop(0) stanza.pop(0)
self.check_stanza(TestStanza, stanza, """ self.check(stanza, """
<foo xmlns="foo"> <foo xmlns="foo">
<foobar qux="b" /> <foobar qux="b" />
</foo> </foo>

View file

@ -7,7 +7,7 @@ class TestErrorStanzas(SleekTest):
"""Test setting initial values in error stanza.""" """Test setting initial values in error stanza."""
msg = self.Message() msg = self.Message()
msg.enable('error') msg.enable('error')
self.check_message(msg, """ self.check(msg, """
<message type="error"> <message type="error">
<error type="cancel"> <error type="cancel">
<feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /> <feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
@ -20,7 +20,7 @@ class TestErrorStanzas(SleekTest):
msg = self.Message() msg = self.Message()
msg['error']['condition'] = 'item-not-found' msg['error']['condition'] = 'item-not-found'
self.check_message(msg, """ self.check(msg, """
<message type="error"> <message type="error">
<error type="cancel"> <error type="cancel">
<item-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /> <item-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
@ -32,7 +32,7 @@ class TestErrorStanzas(SleekTest):
msg['error']['condition'] = 'resource-constraint' msg['error']['condition'] = 'resource-constraint'
self.check_message(msg, """ self.check(msg, """
<message type="error"> <message type="error">
<error type="cancel"> <error type="cancel">
<resource-constraint xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /> <resource-constraint xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
@ -48,7 +48,7 @@ class TestErrorStanzas(SleekTest):
del msg['error']['condition'] del msg['error']['condition']
self.check_message(msg, """ self.check(msg, """
<message type="error"> <message type="error">
<error type="cancel"> <error type="cancel">
<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">Error!</text> <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">Error!</text>
@ -64,7 +64,7 @@ class TestErrorStanzas(SleekTest):
del msg['error']['text'] del msg['error']['text']
self.check_message(msg, """ self.check(msg, """
<message type="error"> <message type="error">
<error type="cancel"> <error type="cancel">
<internal-server-error xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /> <internal-server-error xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />

Binary file not shown.

View file

@ -18,7 +18,7 @@ class TestGmail(SleekTest):
iq['gmail']['newer-than-time'] = '1140638252542' iq['gmail']['newer-than-time'] = '1140638252542'
iq['gmail']['newer-than-tid'] = '11134623426430234' iq['gmail']['newer-than-tid'] = '11134623426430234'
self.check_iq(iq, """ self.check(iq, """
<iq type="get"> <iq type="get">
<query xmlns="google:mail:notify" <query xmlns="google:mail:notify"
newer-than-time="1140638252542" newer-than-time="1140638252542"

View file

@ -11,7 +11,7 @@ class TestIqStanzas(SleekTest):
def testSetup(self): def testSetup(self):
"""Test initializing default Iq values.""" """Test initializing default Iq values."""
iq = self.Iq() iq = self.Iq()
self.check_iq(iq, """ self.check(iq, """
<iq id="0" /> <iq id="0" />
""") """)
@ -19,7 +19,7 @@ class TestIqStanzas(SleekTest):
"""Test setting Iq stanza payload.""" """Test setting Iq stanza payload."""
iq = self.Iq() iq = self.Iq()
iq.setPayload(ET.Element('{test}tester')) iq.setPayload(ET.Element('{test}tester'))
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<tester xmlns="test" /> <tester xmlns="test" />
</iq> </iq>
@ -29,7 +29,7 @@ class TestIqStanzas(SleekTest):
def testUnhandled(self): def testUnhandled(self):
"""Test behavior for Iq.unhandled.""" """Test behavior for Iq.unhandled."""
self.stream_start() self.stream_start()
self.stream_recv(""" self.recv("""
<iq id="test" type="get"> <iq id="test" type="get">
<query xmlns="test" /> <query xmlns="test" />
</iq> </iq>
@ -40,7 +40,7 @@ class TestIqStanzas(SleekTest):
iq['error']['condition'] = 'feature-not-implemented' iq['error']['condition'] = 'feature-not-implemented'
iq['error']['text'] = 'No handlers registered for this request.' iq['error']['text'] = 'No handlers registered for this request.'
self.stream_send_iq(iq, """ self.send(iq, """
<iq id="test" type="error"> <iq id="test" type="error">
<error type="cancel"> <error type="cancel">
<feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" /> <feature-not-implemented xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
@ -56,14 +56,14 @@ class TestIqStanzas(SleekTest):
iq = self.Iq() iq = self.Iq()
iq['query'] = 'query_ns' iq['query'] = 'query_ns'
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<query xmlns="query_ns" /> <query xmlns="query_ns" />
</iq> </iq>
""") """)
iq['query'] = 'query_ns2' iq['query'] = 'query_ns2'
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<query xmlns="query_ns2" /> <query xmlns="query_ns2" />
</iq> </iq>
@ -72,7 +72,7 @@ class TestIqStanzas(SleekTest):
self.failUnless(iq['query'] == 'query_ns2', "Query namespace doesn't match") self.failUnless(iq['query'] == 'query_ns2', "Query namespace doesn't match")
del iq['query'] del iq['query']
self.check_iq(iq, """ self.check(iq, """
<iq id="0" /> <iq id="0" />
""") """)
@ -83,7 +83,7 @@ class TestIqStanzas(SleekTest):
iq['type'] = 'get' iq['type'] = 'get'
iq.reply() iq.reply()
self.check_iq(iq, """ self.check(iq, """
<iq id="0" type="result" /> <iq id="0" type="result" />
""") """)

View file

@ -33,7 +33,7 @@ class TestMessageStanzas(SleekTest):
p = ET.Element('{http://www.w3.org/1999/xhtml}p') p = ET.Element('{http://www.w3.org/1999/xhtml}p')
p.text = "This is the htmlim message" p.text = "This is the htmlim message"
msg['html']['body'] = p msg['html']['body'] = p
self.check_message(msg, """ self.check(msg, """
<message to="fritzy@netflint.net/sleekxmpp" type="chat"> <message to="fritzy@netflint.net/sleekxmpp" type="chat">
<body>this is the plaintext message</body> <body>this is the plaintext message</body>
<html xmlns="http://jabber.org/protocol/xhtml-im"> <html xmlns="http://jabber.org/protocol/xhtml-im">
@ -47,7 +47,7 @@ class TestMessageStanzas(SleekTest):
"Test message/nick/nick stanza." "Test message/nick/nick stanza."
msg = self.Message() msg = self.Message()
msg['nick']['nick'] = 'A nickname!' msg['nick']['nick'] = 'A nickname!'
self.check_message(msg, """ self.check(msg, """
<message> <message>
<nick xmlns="http://jabber.org/nick/nick">A nickname!</nick> <nick xmlns="http://jabber.org/nick/nick">A nickname!</nick>
</message> </message>

View file

@ -8,26 +8,26 @@ class TestPresenceStanzas(SleekTest):
"""Regression check presence['type'] = 'dnd' show value working""" """Regression check presence['type'] = 'dnd' show value working"""
p = self.Presence() p = self.Presence()
p['type'] = 'dnd' p['type'] = 'dnd'
self.check_presence(p, "<presence><show>dnd</show></presence>") self.check(p, "<presence><show>dnd</show></presence>")
def testPresenceType(self): def testPresenceType(self):
"""Test manipulating presence['type']""" """Test manipulating presence['type']"""
p = self.Presence() p = self.Presence()
p['type'] = 'available' p['type'] = 'available'
self.check_presence(p, "<presence />") self.check(p, "<presence />")
self.failUnless(p['type'] == 'available', self.failUnless(p['type'] == 'available',
"Incorrect presence['type'] for type 'available': %s" % p['type']) "Incorrect presence['type'] for type 'available': %s" % p['type'])
for showtype in ['away', 'chat', 'dnd', 'xa']: for showtype in ['away', 'chat', 'dnd', 'xa']:
p['type'] = showtype p['type'] = showtype
self.check_presence(p, """ self.check(p, """
<presence><show>%s</show></presence> <presence><show>%s</show></presence>
""" % showtype) """ % showtype)
self.failUnless(p['type'] == showtype, self.failUnless(p['type'] == showtype,
"Incorrect presence['type'] for type '%s'" % showtype) "Incorrect presence['type'] for type '%s'" % showtype)
p['type'] = None p['type'] = None
self.check_presence(p, "<presence />") self.check(p, "<presence />")
def testPresenceUnsolicitedOffline(self): def testPresenceUnsolicitedOffline(self):
""" """
@ -57,7 +57,7 @@ class TestPresenceStanzas(SleekTest):
"""Test presence/nick/nick stanza.""" """Test presence/nick/nick stanza."""
p = self.Presence() p = self.Presence()
p['nick']['nick'] = 'A nickname!' p['nick']['nick'] = 'A nickname!'
self.check_presence(p, """ self.check(p, """
<presence> <presence>
<nick xmlns="http://jabber.org/nick/nick">A nickname!</nick> <nick xmlns="http://jabber.org/nick/nick">A nickname!</nick>
</presence> </presence>

View file

@ -16,7 +16,7 @@ class TestRosterStanzas(SleekTest):
'name': 'Other User', 'name': 'Other User',
'subscription': 'both', 'subscription': 'both',
'groups': []}}) 'groups': []}})
self.check_iq(iq, """ self.check(iq, """
<iq> <iq>
<query xmlns="jabber:iq:roster"> <query xmlns="jabber:iq:roster">
<item jid="user@example.com" name="User" subscription="both"> <item jid="user@example.com" name="User" subscription="both">
@ -78,7 +78,7 @@ class TestRosterStanzas(SleekTest):
""" """
iq = self.Iq(ET.fromstring(xml_string)) iq = self.Iq(ET.fromstring(xml_string))
del iq['roster']['items'] del iq['roster']['items']
self.check_iq(iq, """ self.check(iq, """
<iq> <iq>
<query xmlns="jabber:iq:roster" /> <query xmlns="jabber:iq:roster" />
</iq> </iq>

View file

@ -14,7 +14,7 @@ class TestDataForms(SleekTest):
msg = self.Message() msg = self.Message()
msg['form']['instructions'] = "Instructions\nSecond batch" msg['form']['instructions'] = "Instructions\nSecond batch"
self.check_message(msg, """ self.check(msg, """
<message> <message>
<x xmlns="jabber:x:data" type="form"> <x xmlns="jabber:x:data" type="form">
<instructions>Instructions</instructions> <instructions>Instructions</instructions>
@ -35,7 +35,7 @@ class TestDataForms(SleekTest):
required=True, required=True,
value='Some text!') value='Some text!')
self.check_message(msg, """ self.check(msg, """
<message> <message>
<x xmlns="jabber:x:data" type="form"> <x xmlns="jabber:x:data" type="form">
<field var="f1" type="text-single" label="Text"> <field var="f1" type="text-single" label="Text">
@ -62,7 +62,7 @@ class TestDataForms(SleekTest):
'value': 'cool'}, 'value': 'cool'},
{'label': 'Urgh!', {'label': 'Urgh!',
'value': 'urgh'}]})] 'value': 'urgh'}]})]
self.check_message(msg, """ self.check(msg, """
<message> <message>
<x xmlns="jabber:x:data" type="form"> <x xmlns="jabber:x:data" type="form">
<field var="f1" type="text-single" label="Username"> <field var="f1" type="text-single" label="Username">
@ -99,7 +99,7 @@ class TestDataForms(SleekTest):
form.setValues({'foo': 'Foo!', form.setValues({'foo': 'Foo!',
'bar': ['a', 'b']}) 'bar': ['a', 'b']})
self.check_message(msg, """ self.check(msg, """
<message> <message>
<x xmlns="jabber:x:data" type="form"> <x xmlns="jabber:x:data" type="form">
<field var="foo" type="text-single"> <field var="foo" type="text-single">

View file

@ -14,7 +14,7 @@ class TestDisco(SleekTest):
iq['id'] = "0" iq['id'] = "0"
iq['disco_info']['node'] = '' iq['disco_info']['node'] = ''
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<query xmlns="http://jabber.org/protocol/disco#info" /> <query xmlns="http://jabber.org/protocol/disco#info" />
</iq> </iq>
@ -26,7 +26,7 @@ class TestDisco(SleekTest):
iq['id'] = "0" iq['id'] = "0"
iq['disco_info']['node'] = 'foo' iq['disco_info']['node'] = 'foo'
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<query xmlns="http://jabber.org/protocol/disco#info" node="foo" /> <query xmlns="http://jabber.org/protocol/disco#info" node="foo" />
</iq> </iq>
@ -38,7 +38,7 @@ class TestDisco(SleekTest):
iq['id'] = "0" iq['id'] = "0"
iq['disco_items']['node'] = '' iq['disco_items']['node'] = ''
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<query xmlns="http://jabber.org/protocol/disco#items" /> <query xmlns="http://jabber.org/protocol/disco#items" />
</iq> </iq>
@ -50,7 +50,7 @@ class TestDisco(SleekTest):
iq['id'] = "0" iq['id'] = "0"
iq['disco_items']['node'] = 'foo' iq['disco_items']['node'] = 'foo'
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<query xmlns="http://jabber.org/protocol/disco#items" node="foo" /> <query xmlns="http://jabber.org/protocol/disco#items" node="foo" />
</iq> </iq>
@ -63,7 +63,7 @@ class TestDisco(SleekTest):
iq['disco_info']['node'] = 'foo' iq['disco_info']['node'] = 'foo'
iq['disco_info'].addIdentity('conference', 'text', 'Chatroom') iq['disco_info'].addIdentity('conference', 'text', 'Chatroom')
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<query xmlns="http://jabber.org/protocol/disco#info" node="foo"> <query xmlns="http://jabber.org/protocol/disco#info" node="foo">
<identity category="conference" type="text" name="Chatroom" /> <identity category="conference" type="text" name="Chatroom" />
@ -79,7 +79,7 @@ class TestDisco(SleekTest):
iq['disco_info'].addFeature('foo') iq['disco_info'].addFeature('foo')
iq['disco_info'].addFeature('bar') iq['disco_info'].addFeature('bar')
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<query xmlns="http://jabber.org/protocol/disco#info" node="foo"> <query xmlns="http://jabber.org/protocol/disco#info" node="foo">
<feature var="foo" /> <feature var="foo" />
@ -97,7 +97,7 @@ class TestDisco(SleekTest):
iq['disco_items'].addItem('user@localhost', 'foo') iq['disco_items'].addItem('user@localhost', 'foo')
iq['disco_items'].addItem('user@localhost', 'bar', 'Testing') iq['disco_items'].addItem('user@localhost', 'bar', 'Testing')
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<query xmlns="http://jabber.org/protocol/disco#items" node="foo"> <query xmlns="http://jabber.org/protocol/disco#items" node="foo">
<item jid="user@localhost" /> <item jid="user@localhost" />

View file

@ -11,7 +11,7 @@ class TestAddresses(SleekTest):
"""Testing adding extended stanza address.""" """Testing adding extended stanza address."""
msg = self.Message() msg = self.Message()
msg['addresses'].addAddress(atype='to', jid='to@header1.org') msg['addresses'].addAddress(atype='to', jid='to@header1.org')
self.check_message(msg, """ self.check(msg, """
<message> <message>
<addresses xmlns="http://jabber.org/protocol/address"> <addresses xmlns="http://jabber.org/protocol/address">
<address jid="to@header1.org" type="to" /> <address jid="to@header1.org" type="to" />
@ -23,7 +23,7 @@ class TestAddresses(SleekTest):
msg['addresses'].addAddress(atype='replyto', msg['addresses'].addAddress(atype='replyto',
jid='replyto@header1.org', jid='replyto@header1.org',
desc='Reply address') desc='Reply address')
self.check_message(msg, """ self.check(msg, """
<message> <message>
<addresses xmlns="http://jabber.org/protocol/address"> <addresses xmlns="http://jabber.org/protocol/address">
<address jid="replyto@header1.org" type="replyto" desc="Reply address" /> <address jid="replyto@header1.org" type="replyto" desc="Reply address" />
@ -53,14 +53,14 @@ class TestAddresses(SleekTest):
'jid':'cc@header2.org'}, 'jid':'cc@header2.org'},
{'type':'bcc', {'type':'bcc',
'jid':'bcc@header2.org'}]) 'jid':'bcc@header2.org'}])
self.check_message(msg, xmlstring) self.check(msg, xmlstring)
msg = self.Message() msg = self.Message()
msg['addresses']['replyto'] = [{'jid':'replyto@header1.org', msg['addresses']['replyto'] = [{'jid':'replyto@header1.org',
'desc':'Reply address'}] 'desc':'Reply address'}]
msg['addresses']['cc'] = [{'jid':'cc@header2.org'}] msg['addresses']['cc'] = [{'jid':'cc@header2.org'}]
msg['addresses']['bcc'] = [{'jid':'bcc@header2.org'}] msg['addresses']['bcc'] = [{'jid':'bcc@header2.org'}]
self.check_message(msg, xmlstring) self.check(msg, xmlstring)
def testAddURI(self): def testAddURI(self):
"""Testing adding URI attribute to extended stanza address.""" """Testing adding URI attribute to extended stanza address."""
@ -69,7 +69,7 @@ class TestAddresses(SleekTest):
addr = msg['addresses'].addAddress(atype='to', addr = msg['addresses'].addAddress(atype='to',
jid='to@header1.org', jid='to@header1.org',
node='foo') node='foo')
self.check_message(msg, """ self.check(msg, """
<message> <message>
<addresses xmlns="http://jabber.org/protocol/address"> <addresses xmlns="http://jabber.org/protocol/address">
<address node="foo" jid="to@header1.org" type="to" /> <address node="foo" jid="to@header1.org" type="to" />
@ -78,7 +78,7 @@ class TestAddresses(SleekTest):
""") """)
addr['uri'] = 'mailto:to@header2.org' addr['uri'] = 'mailto:to@header2.org'
self.check_message(msg, """ self.check(msg, """
<message> <message>
<addresses xmlns="http://jabber.org/protocol/address"> <addresses xmlns="http://jabber.org/protocol/address">
<address type="to" uri="mailto:to@header2.org" /> <address type="to" uri="mailto:to@header2.org" />
@ -99,13 +99,13 @@ class TestAddresses(SleekTest):
msg = self.Message() msg = self.Message()
addr = msg['addresses'].addAddress(jid='to@header1.org', atype='to') addr = msg['addresses'].addAddress(jid='to@header1.org', atype='to')
self.check_message(msg, xmlstring % '') self.check(msg, xmlstring % '')
addr['delivered'] = True addr['delivered'] = True
self.check_message(msg, xmlstring % 'delivered="true"') self.check(msg, xmlstring % 'delivered="true"')
addr['delivered'] = False addr['delivered'] = False
self.check_message(msg, xmlstring % '') self.check(msg, xmlstring % '')
suite = unittest.TestLoader().loadTestsFromTestCase(TestAddresses) suite = unittest.TestLoader().loadTestsFromTestCase(TestAddresses)

View file

@ -16,7 +16,7 @@ class TestPubsubStanzas(SleekTest):
aff2['affiliation'] = 'publisher' aff2['affiliation'] = 'publisher'
iq['pubsub']['affiliations'].append(aff1) iq['pubsub']['affiliations'].append(aff1)
iq['pubsub']['affiliations'].append(aff2) iq['pubsub']['affiliations'].append(aff2)
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<pubsub xmlns="http://jabber.org/protocol/pubsub"> <pubsub xmlns="http://jabber.org/protocol/pubsub">
<affiliations> <affiliations>
@ -38,7 +38,7 @@ class TestPubsubStanzas(SleekTest):
sub2['subscription'] = 'subscribed' sub2['subscription'] = 'subscribed'
iq['pubsub']['subscriptions'].append(sub1) iq['pubsub']['subscriptions'].append(sub1)
iq['pubsub']['subscriptions'].append(sub2) iq['pubsub']['subscriptions'].append(sub2)
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<pubsub xmlns="http://jabber.org/protocol/pubsub"> <pubsub xmlns="http://jabber.org/protocol/pubsub">
<subscriptions> <subscriptions>
@ -55,7 +55,7 @@ class TestPubsubStanzas(SleekTest):
iq['pubsub']['subscription']['node'] = 'testnode alsdkjfas' iq['pubsub']['subscription']['node'] = 'testnode alsdkjfas'
iq['pubsub']['subscription']['jid'] = "fritzy@netflint.net/sleekxmpp" iq['pubsub']['subscription']['jid'] = "fritzy@netflint.net/sleekxmpp"
iq['pubsub']['subscription']['subscription'] = 'unconfigured' iq['pubsub']['subscription']['subscription'] = 'unconfigured'
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<pubsub xmlns="http://jabber.org/protocol/pubsub"> <pubsub xmlns="http://jabber.org/protocol/pubsub">
<subscription node="testnode alsdkjfas" jid="fritzy@netflint.net/sleekxmpp" subscription="unconfigured"> <subscription node="testnode alsdkjfas" jid="fritzy@netflint.net/sleekxmpp" subscription="unconfigured">
@ -88,7 +88,7 @@ class TestPubsubStanzas(SleekTest):
item2['payload'] = payload2 item2['payload'] = payload2
iq['pubsub']['items'].append(item) iq['pubsub']['items'].append(item)
iq['pubsub']['items'].append(item2) iq['pubsub']['items'].append(item2)
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<pubsub xmlns="http://jabber.org/protocol/pubsub"> <pubsub xmlns="http://jabber.org/protocol/pubsub">
<items node="crap"> <items node="crap">
@ -115,7 +115,7 @@ class TestPubsubStanzas(SleekTest):
iq['pubsub']['configure']['form'].addField('pubsub#title', iq['pubsub']['configure']['form'].addField('pubsub#title',
ftype='text-single', ftype='text-single',
value='This thing is awesome') value='This thing is awesome')
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<pubsub xmlns="http://jabber.org/protocol/pubsub"> <pubsub xmlns="http://jabber.org/protocol/pubsub">
<create node="mynode" /> <create node="mynode" />
@ -136,7 +136,7 @@ class TestPubsubStanzas(SleekTest):
iq['psstate']['item']= 'myitem' iq['psstate']['item']= 'myitem'
pl = ET.Element('{http://andyet.net/protocol/pubsubqueue}claimed') pl = ET.Element('{http://andyet.net/protocol/pubsubqueue}claimed')
iq['psstate']['payload'] = pl iq['psstate']['payload'] = pl
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<state xmlns="http://jabber.org/protocol/psstate" node="mynode" item="myitem"> <state xmlns="http://jabber.org/protocol/psstate" node="mynode" item="myitem">
<claimed xmlns="http://andyet.net/protocol/pubsubqueue" /> <claimed xmlns="http://andyet.net/protocol/pubsubqueue" />
@ -152,7 +152,7 @@ class TestPubsubStanzas(SleekTest):
iq['pubsub_owner']['default']['form'].addField('pubsub#title', iq['pubsub_owner']['default']['form'].addField('pubsub#title',
ftype='text-single', ftype='text-single',
value='This thing is awesome') value='This thing is awesome')
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<pubsub xmlns="http://jabber.org/protocol/pubsub#owner"> <pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
<default node="mynode" type="leaf"> <default node="mynode" type="leaf">
@ -176,7 +176,7 @@ class TestPubsubStanzas(SleekTest):
form = xep_0004.Form() form = xep_0004.Form()
form.addField('pubsub#title', ftype='text-single', value='this thing is awesome') form.addField('pubsub#title', ftype='text-single', value='this thing is awesome')
iq['pubsub']['subscribe']['options']['options'] = form iq['pubsub']['subscribe']['options']['options'] = form
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<pubsub xmlns="http://jabber.org/protocol/pubsub"> <pubsub xmlns="http://jabber.org/protocol/pubsub">
<subscribe node="cheese" jid="fritzy@netflint.net/sleekxmpp"> <subscribe node="cheese" jid="fritzy@netflint.net/sleekxmpp">
@ -214,7 +214,7 @@ class TestPubsubStanzas(SleekTest):
iq['pubsub']['publish'].append(item) iq['pubsub']['publish'].append(item)
iq['pubsub']['publish'].append(item2) iq['pubsub']['publish'].append(item2)
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<pubsub xmlns="http://jabber.org/protocol/pubsub"> <pubsub xmlns="http://jabber.org/protocol/pubsub">
<publish node="thingers"> <publish node="thingers">
@ -238,7 +238,7 @@ class TestPubsubStanzas(SleekTest):
"Testing iq/pubsub_owner/delete stanzas" "Testing iq/pubsub_owner/delete stanzas"
iq = self.Iq() iq = self.Iq()
iq['pubsub_owner']['delete']['node'] = 'thingers' iq['pubsub_owner']['delete']['node'] = 'thingers'
self.check_iq(iq, """ self.check(iq, """
<iq id="0"> <iq id="0">
<pubsub xmlns="http://jabber.org/protocol/pubsub#owner"> <pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
<delete node="thingers" /> <delete node="thingers" />
@ -300,7 +300,7 @@ class TestPubsubStanzas(SleekTest):
'label': 'Deliver notification only to available users'}), 'label': 'Deliver notification only to available users'}),
]) ])
self.check_iq(iq, """ self.check(iq, """
<iq to="pubsub.asdf" type="set" id="E" from="fritzy@asdf/87292ede-524d-4117-9076-d934ed3db8e7"> <iq to="pubsub.asdf" type="set" id="E" from="fritzy@asdf/87292ede-524d-4117-9076-d934ed3db8e7">
<pubsub xmlns="http://jabber.org/protocol/pubsub"> <pubsub xmlns="http://jabber.org/protocol/pubsub">
<create node="testnode2" /> <create node="testnode2" />
@ -357,7 +357,7 @@ class TestPubsubStanzas(SleekTest):
msg['pubsub_event']['items'].append(item) msg['pubsub_event']['items'].append(item)
msg['pubsub_event']['items']['node'] = 'cheese' msg['pubsub_event']['items']['node'] = 'cheese'
msg['type'] = 'normal' msg['type'] = 'normal'
self.check_message(msg, """ self.check(msg, """
<message type="normal"> <message type="normal">
<event xmlns="http://jabber.org/protocol/pubsub#event"> <event xmlns="http://jabber.org/protocol/pubsub#event">
<items node="cheese"> <items node="cheese">
@ -383,7 +383,7 @@ class TestPubsubStanzas(SleekTest):
msg['pubsub_event']['items'].append(item2) msg['pubsub_event']['items'].append(item2)
msg['pubsub_event']['items']['node'] = 'cheese' msg['pubsub_event']['items']['node'] = 'cheese'
msg['type'] = 'normal' msg['type'] = 'normal'
self.check_message(msg, """ self.check(msg, """
<message type="normal"> <message type="normal">
<event xmlns="http://jabber.org/protocol/pubsub#event"> <event xmlns="http://jabber.org/protocol/pubsub#event">
<items node="cheese"> <items node="cheese">
@ -415,7 +415,7 @@ class TestPubsubStanzas(SleekTest):
msg['pubsub_event']['items'].append(item2) msg['pubsub_event']['items'].append(item2)
msg['pubsub_event']['items']['node'] = 'cheese' msg['pubsub_event']['items']['node'] = 'cheese'
msg['type'] = 'normal' msg['type'] = 'normal'
self.check_message(msg, """ self.check(msg, """
<message type="normal"> <message type="normal">
<event xmlns="http://jabber.org/protocol/pubsub#event"> <event xmlns="http://jabber.org/protocol/pubsub#event">
<items node="cheese"> <items node="cheese">
@ -435,7 +435,7 @@ class TestPubsubStanzas(SleekTest):
msg['pubsub_event']['collection']['associate']['node'] = 'cheese' msg['pubsub_event']['collection']['associate']['node'] = 'cheese'
msg['pubsub_event']['collection']['node'] = 'cheeseburger' msg['pubsub_event']['collection']['node'] = 'cheeseburger'
msg['type'] = 'headline' msg['type'] = 'headline'
self.check_message(msg, """ self.check(msg, """
<message type="headline"> <message type="headline">
<event xmlns="http://jabber.org/protocol/pubsub#event"> <event xmlns="http://jabber.org/protocol/pubsub#event">
<collection node="cheeseburger"> <collection node="cheeseburger">
@ -450,7 +450,7 @@ class TestPubsubStanzas(SleekTest):
msg['pubsub_event']['collection']['disassociate']['node'] = 'cheese' msg['pubsub_event']['collection']['disassociate']['node'] = 'cheese'
msg['pubsub_event']['collection']['node'] = 'cheeseburger' msg['pubsub_event']['collection']['node'] = 'cheeseburger'
msg['type'] = 'headline' msg['type'] = 'headline'
self.check_message(msg, """ self.check(msg, """
<message type="headline"> <message type="headline">
<event xmlns="http://jabber.org/protocol/pubsub#event"> <event xmlns="http://jabber.org/protocol/pubsub#event">
<collection node="cheeseburger"> <collection node="cheeseburger">
@ -467,7 +467,7 @@ class TestPubsubStanzas(SleekTest):
ftype='text-single', ftype='text-single',
value='This thing is awesome') value='This thing is awesome')
msg['type'] = 'headline' msg['type'] = 'headline'
self.check_message(msg, """ self.check(msg, """
<message type="headline"> <message type="headline">
<event xmlns="http://jabber.org/protocol/pubsub#event"> <event xmlns="http://jabber.org/protocol/pubsub#event">
<configuration node="cheese"> <configuration node="cheese">
@ -485,7 +485,7 @@ class TestPubsubStanzas(SleekTest):
msg = self.Message() msg = self.Message()
msg['pubsub_event']['purge']['node'] = 'pickles' msg['pubsub_event']['purge']['node'] = 'pickles'
msg['type'] = 'headline' msg['type'] = 'headline'
self.check_message(msg, """ self.check(msg, """
<message type="headline"> <message type="headline">
<event xmlns="http://jabber.org/protocol/pubsub#event"> <event xmlns="http://jabber.org/protocol/pubsub#event">
<purge node="pickles" /> <purge node="pickles" />
@ -501,7 +501,7 @@ class TestPubsubStanzas(SleekTest):
msg['pubsub_event']['subscription']['subscription'] = 'subscribed' msg['pubsub_event']['subscription']['subscription'] = 'subscribed'
msg['pubsub_event']['subscription']['expiry'] = 'presence' msg['pubsub_event']['subscription']['expiry'] = 'presence'
msg['type'] = 'headline' msg['type'] = 'headline'
self.check_message(msg, """ self.check(msg, """
<message type="headline"> <message type="headline">
<event xmlns="http://jabber.org/protocol/pubsub#event"> <event xmlns="http://jabber.org/protocol/pubsub#event">
<subscription node="pickles" subid="aabb1122" jid="fritzy@netflint.net/test" subscription="subscribed" expiry="presence" /> <subscription node="pickles" subid="aabb1122" jid="fritzy@netflint.net/test" subscription="subscribed" expiry="presence" />

View file

@ -21,24 +21,24 @@ class TestChatStates(SleekTest):
msg = self.Message() msg = self.Message()
msg['chat_state'].active() msg['chat_state'].active()
self.check_message(msg, xmlstring % 'active', self.check(msg, xmlstring % 'active',
use_values=False) use_values=False)
msg['chat_state'].composing() msg['chat_state'].composing()
self.check_message(msg, xmlstring % 'composing', self.check(msg, xmlstring % 'composing',
use_values=False) use_values=False)
msg['chat_state'].gone() msg['chat_state'].gone()
self.check_message(msg, xmlstring % 'gone', self.check(msg, xmlstring % 'gone',
use_values=False) use_values=False)
msg['chat_state'].inactive() msg['chat_state'].inactive()
self.check_message(msg, xmlstring % 'inactive', self.check(msg, xmlstring % 'inactive',
use_values=False) use_values=False)
msg['chat_state'].paused() msg['chat_state'].paused()
self.check_message(msg, xmlstring % 'paused', self.check(msg, xmlstring % 'paused',
use_values=False) use_values=False)
suite = unittest.TestLoader().loadTestsFromTestCase(TestChatStates) suite = unittest.TestLoader().loadTestsFromTestCase(TestChatStates)

View file

@ -19,13 +19,13 @@ class TestStreamTester(SleekTest):
self.xmpp.add_event_handler('message', echo) self.xmpp.add_event_handler('message', echo)
self.stream_recv(""" self.recv("""
<message to="tester@localhost" from="user@localhost"> <message to="tester@localhost" from="user@localhost">
<body>Hi!</body> <body>Hi!</body>
</message> </message>
""") """)
self.stream_send_message(""" self.send("""
<message to="user@localhost"> <message to="user@localhost">
<body>Thanks for sending: Hi!</body> <body>Thanks for sending: Hi!</body>
</message> </message>
@ -40,13 +40,13 @@ class TestStreamTester(SleekTest):
self.xmpp.add_event_handler('message', echo) self.xmpp.add_event_handler('message', echo)
self.stream_recv(""" self.recv("""
<message to="tester.localhost" from="user@localhost"> <message to="tester.localhost" from="user@localhost">
<body>Hi!</body> <body>Hi!</body>
</message> </message>
""") """)
self.stream_send_message(""" self.send("""
<message to="user@localhost" from="tester.localhost"> <message to="user@localhost" from="tester.localhost">
<body>Thanks for sending: Hi!</body> <body>Thanks for sending: Hi!</body>
</message> </message>
@ -55,6 +55,6 @@ class TestStreamTester(SleekTest):
def testSendStreamHeader(self): def testSendStreamHeader(self):
"""Test that we can check a sent stream header.""" """Test that we can check a sent stream header."""
self.stream_start(mode='client', skip=False) self.stream_start(mode='client', skip=False)
self.stream_send_header(sto='localhost') self.send_header(sto='localhost')
suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamTester) suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamTester)

View file

@ -26,13 +26,13 @@ class TestStreamExceptions(SleekTest):
self.stream_start() self.stream_start()
self.xmpp.add_event_handler('message', message) self.xmpp.add_event_handler('message', message)
self.stream_recv(""" self.recv("""
<message> <message>
<body>This is going to cause an error.</body> <body>This is going to cause an error.</body>
</message> </message>
""") """)
self.stream_send_message(""" self.send("""
<message type="error"> <message type="error">
<error type="cancel"> <error type="cancel">
<feature-not-implemented <feature-not-implemented
@ -57,13 +57,13 @@ class TestStreamExceptions(SleekTest):
self.xmpp.add_event_handler('message', message, self.xmpp.add_event_handler('message', message,
threaded=True) threaded=True)
self.stream_recv(""" self.recv("""
<message> <message>
<body>This is going to cause an error.</body> <body>This is going to cause an error.</body>
</message> </message>
""") """)
self.stream_send_message(""" self.send("""
<message type="error"> <message type="error">
<error type="cancel"> <error type="cancel">
<feature-not-implemented <feature-not-implemented
@ -84,14 +84,14 @@ class TestStreamExceptions(SleekTest):
self.stream_start() self.stream_start()
self.xmpp.add_event_handler('message', message) self.xmpp.add_event_handler('message', message)
self.stream_recv(""" self.recv("""
<message> <message>
<body>This is going to cause an error.</body> <body>This is going to cause an error.</body>
</message> </message>
""") """)
if sys.version_info < (3, 0): if sys.version_info < (3, 0):
self.stream_send_message(""" self.send("""
<message type="error"> <message type="error">
<error type="cancel"> <error type="cancel">
<undefined-condition <undefined-condition

View file

@ -30,11 +30,11 @@ class TestHandlers(SleekTest):
self.xmpp.registerHandler(callback) self.xmpp.registerHandler(callback)
self.stream_recv("""<tester xmlns="test" />""") self.recv("""<tester xmlns="test" />""")
msg = self.Message() msg = self.Message()
msg['body'] = 'Success!' msg['body'] = 'Success!'
self.stream_send_message(msg) self.send(msg)
def testWaiter(self): def testWaiter(self):
"""Test using stream waiter handler.""" """Test using stream waiter handler."""
@ -55,7 +55,7 @@ class TestHandlers(SleekTest):
self.xmpp.add_event_handler('message', waiter_handler, threaded=True) self.xmpp.add_event_handler('message', waiter_handler, threaded=True)
# Send message to trigger waiter_handler # Send message to trigger waiter_handler
self.stream_recv(""" self.recv("""
<message> <message>
<body>Testing</body> <body>Testing</body>
</message> </message>
@ -66,10 +66,10 @@ class TestHandlers(SleekTest):
iq['id'] = 'test' iq['id'] = 'test'
iq['type'] = 'set' iq['type'] = 'set'
iq['query'] = 'test' iq['query'] = 'test'
self.stream_send_iq(iq) self.send(iq)
# Send the reply Iq # Send the reply Iq
self.stream_recv(""" self.recv("""
<iq id="test" type="result"> <iq id="test" type="result">
<query xmlns="test" /> <query xmlns="test" />
</iq> </iq>
@ -78,7 +78,7 @@ class TestHandlers(SleekTest):
# Check that waiter_handler received the reply # Check that waiter_handler received the reply
msg = self.Message() msg = self.Message()
msg['body'] = 'Successful: test' msg['body'] = 'Successful: test'
self.stream_send_message(msg) self.send(msg)
def testWaiterTimeout(self): def testWaiterTimeout(self):
"""Test that waiter handler is removed after timeout.""" """Test that waiter handler is removed after timeout."""
@ -93,14 +93,14 @@ class TestHandlers(SleekTest):
self.xmpp.add_event_handler('message', waiter_handler, threaded=True) self.xmpp.add_event_handler('message', waiter_handler, threaded=True)
# Start test by triggerig waiter_handler # Start test by triggerig waiter_handler
self.stream_recv("""<message><body>Start Test</body></message>""") self.recv("""<message><body>Start Test</body></message>""")
# Check that Iq was sent to trigger start of timeout period # Check that Iq was sent to trigger start of timeout period
iq = self.Iq() iq = self.Iq()
iq['id'] = 'test2' iq['id'] = 'test2'
iq['type'] = 'set' iq['type'] = 'set'
iq['query'] = 'test2' iq['query'] = 'test2'
self.stream_send_iq(iq) self.send(iq)
# Check that the waiter is no longer registered # Check that the waiter is no longer registered
waiter_exists = self.xmpp.removeHandler('IqWait_test2') waiter_exists = self.xmpp.removeHandler('IqWait_test2')

View file

@ -29,10 +29,10 @@ class TestStreamPresence(SleekTest):
self.xmpp.add_event_handler('got_offline', got_offline) self.xmpp.add_event_handler('got_offline', got_offline)
self.xmpp.add_event_handler('presence_unavailable', unavailable) self.xmpp.add_event_handler('presence_unavailable', unavailable)
self.stream_recv(""" self.recv("""
<presence from="otheruser@localhost" <presence type="unavailable"
to="tester@localhost" from="otheruser@localhost"
type="unavailable" /> to="tester@localhost"/>
""") """)
# Give event queue time to process. # Give event queue time to process.
@ -56,7 +56,7 @@ class TestStreamPresence(SleekTest):
# #
# We use the stream to initialize the roster to make # We use the stream to initialize the roster to make
# the test independent of the roster implementation. # the test independent of the roster implementation.
self.stream_recv(""" self.recv("""
<iq type="set"> <iq type="set">
<query xmlns="jabber:iq:roster"> <query xmlns="jabber:iq:roster">
<item jid="otheruser@localhost" <item jid="otheruser@localhost"
@ -69,13 +69,13 @@ class TestStreamPresence(SleekTest):
""") """)
# Contact comes online. # Contact comes online.
self.stream_recv(""" self.recv("""
<presence from="otheruser@localhost/foobar" <presence from="otheruser@localhost/foobar"
to="tester@localhost" /> to="tester@localhost" />
""") """)
# Contact goes offline, should trigger got_offline. # Contact goes offline, should trigger got_offline.
self.stream_recv(""" self.recv("""
<presence from="otheruser@localhost/foobar" <presence from="otheruser@localhost/foobar"
to="tester@localhost" to="tester@localhost"
type="unavailable" /> type="unavailable" />
@ -102,7 +102,7 @@ class TestStreamPresence(SleekTest):
self.xmpp.add_event_handler('presence_available', presence_available) self.xmpp.add_event_handler('presence_available', presence_available)
self.xmpp.add_event_handler('got_online', got_online) self.xmpp.add_event_handler('got_online', got_online)
self.stream_recv(""" self.recv("""
<presence from="user@localhost" <presence from="user@localhost"
to="tester@localhost" /> to="tester@localhost" />
""") """)
@ -140,22 +140,22 @@ class TestStreamPresence(SleekTest):
self.xmpp.auto_authorize = True self.xmpp.auto_authorize = True
self.xmpp.auto_subscribe = True self.xmpp.auto_subscribe = True
self.stream_recv(""" self.recv("""
<presence from="user@localhost" <presence from="user@localhost"
to="tester@localhost" to="tester@localhost"
type="subscribe" /> type="subscribe" />
""") """)
self.stream_send_presence(""" self.send("""
<presence to="user@localhost" <presence to="user@localhost"
type="subscribed" /> type="subscribed" />
""") """)
self.stream_send_presence(""" self.send("""
<presence to="user@localhost" /> <presence to="user@localhost" />
""") """)
self.stream_send_presence(""" self.send("""
<presence to="user@localhost" <presence to="user@localhost"
type="subscribe" /> type="subscribe" />
""") """)
@ -185,13 +185,13 @@ class TestStreamPresence(SleekTest):
# With this setting we should reject all subscriptions. # With this setting we should reject all subscriptions.
self.xmpp.roster['tester@localhost'].auto_authorize = False self.xmpp.roster['tester@localhost'].auto_authorize = False
self.stream_recv(""" self.recv("""
<presence from="user@localhost" <presence from="user@localhost"
to="tester@localhost" to="tester@localhost"
type="subscribe" /> type="subscribe" />
""") """)
self.stream_send_presence(""" self.send("""
<presence to="user@localhost" <presence to="user@localhost"
type="unsubscribed" /> type="unsubscribed" />
""") """)

View file

@ -19,12 +19,12 @@ class TestStreamRoster(SleekTest):
t = threading.Thread(name='get_roster', target=self.xmpp.get_roster) t = threading.Thread(name='get_roster', target=self.xmpp.get_roster)
t.start() t.start()
self.stream_send_iq(""" self.send("""
<iq type="get" id="1"> <iq type="get" id="1">
<query xmlns="jabber:iq:roster" /> <query xmlns="jabber:iq:roster" />
</iq> </iq>
""") """)
self.stream_recv(""" self.recv("""
<iq to='tester@localhost' type="result" id="1"> <iq to='tester@localhost' type="result" id="1">
<query xmlns="jabber:iq:roster"> <query xmlns="jabber:iq:roster">
<item jid="user@localhost" <item jid="user@localhost"
@ -41,7 +41,6 @@ class TestStreamRoster(SleekTest):
# Wait for get_roster to return. # Wait for get_roster to return.
t.join() t.join()
print self.xmpp.roster['tester@localhost']['user@localhost']._state
self.check_roster('tester@localhost', 'user@localhost', self.check_roster('tester@localhost', 'user@localhost',
name='User', name='User',
subscription='from', subscription='from',
@ -53,7 +52,7 @@ class TestStreamRoster(SleekTest):
"""Test handling pushed roster updates.""" """Test handling pushed roster updates."""
self.stream_start(mode='client', jid='tester@localhost') self.stream_start(mode='client', jid='tester@localhost')
self.stream_recv(""" self.recv("""
<iq to='tester@localhost' type="set" id="1"> <iq to='tester@localhost' type="set" id="1">
<query xmlns="jabber:iq:roster"> <query xmlns="jabber:iq:roster">
<item jid="user@localhost" <item jid="user@localhost"
@ -65,7 +64,7 @@ class TestStreamRoster(SleekTest):
</query> </query>
</iq> </iq>
""") """)
self.stream_send_iq(""" self.send("""
<iq type="result" id="1"> <iq type="result" id="1">
<query xmlns="jabber:iq:roster" /> <query xmlns="jabber:iq:roster" />
</iq> </iq>