"""
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
See the file LICENSE for copying permission.
"""
import logging
import sleekxmpp
from optparse import OptionParser
from xml.etree import cElementTree as ET
import os
import time
import sys
import Queue
import thread
class testps(sleekxmpp.ClientXMPP):
def __init__(self, jid, password, ssl=False, plugin_config = {}, plugin_whitelist=[], nodenum=0, pshost=None):
sleekxmpp.ClientXMPP.__init__(self, jid, password, ssl, plugin_config, plugin_whitelist)
self.registerPlugin('xep_0004')
self.registerPlugin('xep_0030')
self.registerPlugin('xep_0060')
self.registerPlugin('xep_0092')
self.add_handler("", self.pubsubEventHandler, name='Pubsub Event', threaded=True)
self.add_event_handler("session_start", self.start, threaded=True)
self.add_handler("", self.handleError, name='Iq Error')
self.events = Queue.Queue()
self.default_config = None
self.ps = self.plugin['xep_0060']
self.node = "pstestnode_%s"
self.pshost = pshost
if pshost is None:
self.pshost = self.boundjid.host
self.nodenum = int(nodenum)
self.leafnode = self.nodenum + 1
self.collectnode = self.nodenum + 2
self.lasterror = ''
self.sprintchars = 0
self.defaultconfig = None
self.tests = ['test_defaultConfig', 'test_createDefaultNode', 'test_getNodes', 'test_deleteNode', 'test_createWithConfig', 'test_reconfigureNode', 'test_subscribeToNode', 'test_addItem', 'test_updateItem', 'test_deleteItem', 'test_unsubscribeNode', 'test_createCollection', 'test_subscribeCollection', 'test_addNodeCollection', 'test_deleteNodeCollection', 'test_addCollectionNode', 'test_deleteCollectionNode', 'test_unsubscribeNodeCollection', 'test_deleteCollection']
self.passed = 0
self.width = 120
def start(self, event):
#TODO: make this configurable
self.getRoster()
self.sendPresence(ppriority=20)
thread.start_new(self.test_all, tuple())
def sprint(self, msg, end=False, color=False):
length = len(msg)
if color:
if color == "red":
color = "1;31"
elif color == "green":
color = "0;32"
msg = "%s%s%s" % ("\033[%sm" % color, msg, "\033[0m")
if not end:
sys.stdout.write(msg)
self.sprintchars += length
else:
self.sprint("%s%s" % ("." * (self.width - self.sprintchars - length), msg))
print('')
self.sprintchars = 0
sys.stdout.flush()
def pubsubEventHandler(self, xml):
for item in xml.findall('{http://jabber.org/protocol/pubsub#event}event/{http://jabber.org/protocol/pubsub#event}items/{http://jabber.org/protocol/pubsub#event}item'):
self.events.put(item.get('id', '__unknown__'))
for item in xml.findall('{http://jabber.org/protocol/pubsub#event}event/{http://jabber.org/protocol/pubsub#event}items/{http://jabber.org/protocol/pubsub#event}retract'):
self.events.put(item.get('id', '__unknown__'))
for item in xml.findall('{http://jabber.org/protocol/pubsub#event}event/{http://jabber.org/protocol/pubsub#event}collection/{http://jabber.org/protocol/pubsub#event}disassociate'):
self.events.put(item.get('node', '__unknown__'))
for item in xml.findall('{http://jabber.org/protocol/pubsub#event}event/{http://jabber.org/protocol/pubsub#event}collection/{http://jabber.org/protocol/pubsub#event}associate'):
self.events.put(item.get('node', '__unknown__'))
def handleError(self, xml):
error = xml.find('{jabber:client}error')
self.lasterror = error.getchildren()[0].tag.split('}')[-1]
def test_all(self):
print("Running Publish-Subscribe Tests")
version = self.plugin['xep_0092'].getVersion(self.pshost)
if version:
print("%s %s on %s" % (version.get('name', 'Unknown Server'), version.get('version', 'v?'), version.get('os', 'Unknown OS')))
print("=" * self.width)
for test in self.tests:
testfunc = getattr(self, test)
self.sprint("%s" % testfunc.__doc__)
if testfunc():
self.sprint("Passed", True, "green")
self.passed += 1
else:
if not self.lasterror:
self.lasterror = 'No response'
self.sprint("Failed (%s)" % self.lasterror, True, "red")
self.lasterror = ''
print("=" * self.width)
self.sprint("Cleaning up...")
#self.ps.deleteNode(self.pshost, self.node % self.nodenum)
self.ps.deleteNode(self.pshost, self.node % self.leafnode)
#self.ps.deleteNode(self.pshost, self.node % self.collectnode)
self.sprint("Done", True, "green")
self.disconnect()
self.sprint("%s" % self.passed, False, "green")
self.sprint("/%s Passed -- " % len(self.tests))
if len(self.tests) - self.passed:
self.sprint("%s" % (len(self.tests) - self.passed), False, "red")
else:
self.sprint("%s" % (len(self.tests) - self.passed), False, "green")
self.sprint(" Failed Tests")
print
#print "%s/%s Passed -- %s Failed Tests" % (self.passed, len(self.tests), len(self.tests) - self.passed)
def test_defaultConfig(self):
"Retreiving default configuration"
result = self.ps.getNodeConfig(self.pshost)
if result is False or result is None:
return False
else:
self.defaultconfig = result
try:
self.defaultconfig.field['pubsub#access_model'].setValue('open')
except KeyError:
pass
try:
self.defaultconfig.field['pubsub#notify_retract'].setValue(True)
except KeyError:
pass
return True
def test_createDefaultNode(self):
"Creating default node"
return self.ps.create_node(self.pshost, self.node % self.nodenum)
def test_getNodes(self):
"Getting list of nodes"
self.ps.getNodes(self.pshost)
self.ps.getItems(self.pshost, 'blog')
return True
def test_deleteNode(self):
"Deleting node"
return self.ps.deleteNode(self.pshost, self.node % self.nodenum)
def test_createWithConfig(self):
"Creating node with config"
if self.defaultconfig is None:
self.lasterror = "No Avail Config"
return False
return self.ps.create_node(self.pshost, self.node % self.leafnode, self.defaultconfig)
def test_reconfigureNode(self):
"Retrieving node config and reconfiguring"
nconfig = self.ps.getNodeConfig(self.pshost, self.node % self.leafnode)
if nconfig == False:
return False
return self.ps.setNodeConfig(self.pshost, self.node % self.leafnode, nconfig)
def test_subscribeToNode(self):
"Subscribing to node"
return self.ps.subscribe(self.pshost, self.node % self.leafnode)
def test_addItem(self):
"Adding item, waiting for notification"
item = ET.Element('test')
result = self.ps.setItem(self.pshost, self.node % self.leafnode, (('test_node1', item),))
if result == False:
return False
try:
event = self.events.get(True, 10)
except Queue.Empty:
return False
if event == 'test_node1':
return True
return False
def test_updateItem(self):
"Updating item, waiting for notification"
item = ET.Element('test')
item.attrib['crap'] = 'yup, right here'
result = self.ps.setItem(self.pshost, self.node % self.leafnode, (('test_node1', item),))
if result == False:
return False
try:
event = self.events.get(True, 10)
except Queue.Empty:
return False
if event == 'test_node1':
return True
return False
def test_deleteItem(self):
"Deleting item, waiting for notification"
result = self.ps.deleteItem(self.pshost, self.node % self.leafnode, 'test_node1')
if result == False:
return False
try:
event = self.events.get(True, 10)
except Queue.Empty:
self.lasterror = "No Notification"
return False
if event == 'test_node1':
return True
return False
def test_unsubscribeNode(self):
"Unsubscribing from node"
return self.ps.unsubscribe(self.pshost, self.node % self.leafnode)
def test_createCollection(self):
"Creating collection node"
return self.ps.create_node(self.pshost, self.node % self.collectnode, self.defaultconfig, True)
def test_subscribeCollection(self):
"Subscribing to collection node"
return self.ps.subscribe(self.pshost, self.node % self.collectnode)
def test_addNodeCollection(self):
"Assigning node to collection, waiting for notification"
config = self.ps.getNodeConfig(self.pshost, self.node % self.leafnode)
if not config or config is None:
self.lasterror = "Config Error"
return False
try:
config.field['pubsub#collection'].setValue(self.node % self.collectnode)
except KeyError:
self.sprint("...Missing Field...", False, "red")
config.addField('pubsub#collection', value=self.node % self.collectnode)
if not self.ps.setNodeConfig(self.pshost, self.node % self.leafnode, config):
return False
try:
event = self.events.get(True, 10)
except Queue.Empty:
self.lasterror = "No Notification"
return False
if event == self.node % self.leafnode:
return True
return False
def test_deleteNodeCollection(self):
"Removing node assignment to collection, waiting for notification"
config = self.ps.getNodeConfig(self.pshost, self.node % self.leafnode)
if not config or config is None:
self.lasterror = "Config Error"
return False
try:
config.field['pubsub#collection'].delValue(self.node % self.collectnode)
except KeyError:
self.sprint("...Missing Field...", False, "red")
config.addField('pubsub#collection', value='')
if not self.ps.setNodeConfig(self.pshost, self.node % self.leafnode, config):
return False
try:
event = self.events.get(True, 10)
except Queue.Empty:
self.lasterror = "No Notification"
return False
if event == self.node % self.leafnode:
return True
return False
def test_addCollectionNode(self):
"Assigning node from collection, waiting for notification"
config = self.ps.getNodeConfig(self.pshost, self.node % self.collectnode)
if not config or config is None:
self.lasterror = "Config Error"
return False
try:
config.field['pubsub#children'].setValue(self.node % self.leafnode)
except KeyError:
self.sprint("...Missing Field...", False, "red")
config.addField('pubsub#children', value=self.node % self.leafnode)
if not self.ps.setNodeConfig(self.pshost, self.node % self.collectnode, config):
return False
try:
event = self.events.get(True, 10)
except Queue.Empty:
self.lasterror = "No Notification"
return False
if event == self.node % self.leafnode:
return True
return False
def test_deleteCollectionNode(self):
"Removing node from collection, waiting for notification"
config = self.ps.getNodeConfig(self.pshost, self.node % self.collectnode)
if not config or config is None:
self.lasterror = "Config Error"
return False
try:
config.field['pubsub#children'].delValue(self.node % self.leafnode)
except KeyError:
self.sprint("...Missing Field...", False, "red")
config.addField('pubsub#children', value='')
if not self.ps.setNodeConfig(self.pshost, self.node % self.collectnode, config):
return False
try:
event = self.events.get(True, 10)
except Queue.Empty:
self.lasterror = "No Notification"
return False
if event == self.node % self.leafnode:
return True
return False
def test_unsubscribeNodeCollection(self):
"Unsubscribing from collection"
return self.ps.unsubscribe(self.pshost, self.node % self.collectnode)
def test_deleteCollection(self):
"Deleting collection"
return self.ps.deleteNode(self.pshost, self.node % self.collectnode)
if __name__ == '__main__':
#parse command line arguements
optp = OptionParser()
optp.add_option('-q','--quiet', help='set logging to ERROR', action='store_const', dest='loglevel', const=logging.ERROR, default=logging.INFO)
optp.add_option('-d','--debug', help='set logging to DEBUG', action='store_const', dest='loglevel', const=logging.DEBUG, default=logging.INFO)
optp.add_option('-v','--verbose', help='set logging to COMM', action='store_const', dest='loglevel', const=5, default=logging.INFO)
optp.add_option("-c","--config", dest="configfile", default="config.xml", help="set config file to use")
optp.add_option("-n","--nodenum", dest="nodenum", default="1", help="set node number to use")
optp.add_option("-p","--pubsub", dest="pubsub", default="1", help="set pubsub host to use")
opts,args = optp.parse_args()
logging.basicConfig(level=opts.loglevel, format='%(levelname)-8s %(message)s')
#load xml config
logging.info("Loading config file: %s" , opts.configfile)
config = ET.parse(os.path.expanduser(opts.configfile)).find('auth')
#init
logging.info("Logging in as %s" , config.attrib['jid'])
plugin_config = {}
plugin_config['xep_0092'] = {'name': 'SleekXMPP Example', 'version': '0.1-dev'}
plugin_config['xep_0199'] = {'keepalive': True, 'timeout': 30, 'frequency': 300}
con = testps(config.attrib['jid'], config.attrib['pass'], plugin_config=plugin_config, plugin_whitelist=[], nodenum=opts.nodenum, pshost=opts.pubsub)
if not config.get('server', None):
# we don't know the server, but the lib can probably figure it out
con.connect()
else:
con.connect((config.attrib['server'], 5222))
con.process(threaded=False)
print("")