summaryrefslogtreecommitdiff
path: root/sleekxmpp
diff options
context:
space:
mode:
authorLance Stout <lancestout@gmail.com>2010-11-17 02:00:20 -0500
committerLance Stout <lancestout@gmail.com>2010-11-17 02:01:12 -0500
commit26aca2b789f62123819041229ba40ab10268f49e (patch)
tree20388fb0559ed85f7143eb3b8a669142cdc43f6b /sleekxmpp
parent4f69a03bb1a8caab7899bd04f97b7975a78f2f34 (diff)
parent5424ede413393db5b835686e8544a9b703fb113b (diff)
downloadslixmpp-26aca2b789f62123819041229ba40ab10268f49e.tar.gz
slixmpp-26aca2b789f62123819041229ba40ab10268f49e.tar.bz2
slixmpp-26aca2b789f62123819041229ba40ab10268f49e.tar.xz
slixmpp-26aca2b789f62123819041229ba40ab10268f49e.zip
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
Diffstat (limited to 'sleekxmpp')
-rw-r--r--sleekxmpp/__init__$py.classbin0 -> 3377 bytes
-rw-r--r--sleekxmpp/basexmpp.py37
-rw-r--r--sleekxmpp/clientxmpp.py31
-rw-r--r--sleekxmpp/componentxmpp.py7
-rw-r--r--sleekxmpp/plugins/__init__.py4
-rw-r--r--sleekxmpp/plugins/gmail_notify.py25
-rw-r--r--sleekxmpp/plugins/jobs.py10
-rw-r--r--sleekxmpp/plugins/old_0004.py80
-rw-r--r--sleekxmpp/plugins/xep_0004.py27
-rw-r--r--sleekxmpp/plugins/xep_0012.py7
-rw-r--r--sleekxmpp/plugins/xep_0030.py608
-rw-r--r--sleekxmpp/plugins/xep_0045.py89
-rw-r--r--sleekxmpp/plugins/xep_0060.py40
-rw-r--r--sleekxmpp/plugins/xep_0078.py17
-rw-r--r--sleekxmpp/plugins/xep_0085.py23
-rw-r--r--sleekxmpp/plugins/xep_0199.py98
-rw-r--r--sleekxmpp/plugins/xep_0202.py7
-rw-r--r--sleekxmpp/roster.py3
-rw-r--r--sleekxmpp/stanza/rootstanza.py5
-rw-r--r--sleekxmpp/test/sleektest.py282
-rw-r--r--sleekxmpp/thirdparty/statemachine.py96
-rw-r--r--sleekxmpp/xmlstream/handler/waiter.py5
-rw-r--r--sleekxmpp/xmlstream/matcher/xmlmask.py8
-rw-r--r--sleekxmpp/xmlstream/scheduler.py9
-rw-r--r--sleekxmpp/xmlstream/stanzabase.py5
-rw-r--r--sleekxmpp/xmlstream/xmlstream.py122
26 files changed, 814 insertions, 831 deletions
diff --git a/sleekxmpp/__init__$py.class b/sleekxmpp/__init__$py.class
new file mode 100644
index 00000000..6b5e86cf
--- /dev/null
+++ b/sleekxmpp/__init__$py.class
Binary files differ
diff --git a/sleekxmpp/basexmpp.py b/sleekxmpp/basexmpp.py
index 15a91564..46c8985a 100644
--- a/sleekxmpp/basexmpp.py
+++ b/sleekxmpp/basexmpp.py
@@ -27,13 +27,7 @@ from sleekxmpp.xmlstream.matcher import *
from sleekxmpp.xmlstream.handler import *
-# Flag indicating if DNS SRV records are available for use.
-SRV_SUPPORT = True
-try:
- import dns.resolver
-except:
- SRV_SUPPORT = False
-
+log = logging.getLogger(__name__)
# In order to make sure that Unicode is handled properly
# in Python 2.x, reset the default encoding.
@@ -205,9 +199,9 @@ class BaseXMPP(XMLStream):
xep = "(XEP-%s) " % self.plugin[plugin].xep
desc = (xep, self.plugin[plugin].description)
- logging.debug("Loaded Plugin %s%s" % desc)
+ log.debug("Loaded Plugin %s%s" % desc)
except:
- logging.exception("Unable to load plugin: %s", plugin)
+ log.exception("Unable to load plugin: %s", plugin)
def register_plugins(self):
"""
@@ -241,7 +235,7 @@ class BaseXMPP(XMLStream):
if key in self.plugin:
return self.plugin[key]
else:
- logging.warning("""Plugin "%s" is not loaded.""" % key)
+ log.warning("""Plugin "%s" is not loaded.""" % key)
return False
def get(self, key, default):
@@ -459,12 +453,12 @@ class BaseXMPP(XMLStream):
"""
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
@jid.setter
def jid(self, value):
- logging.warning("jid property deprecated. Use boundjid.bare")
+ log.warning("jid property deprecated. Use boundjid.bare")
self.boundjid.bare = value
@property
@@ -472,12 +466,12 @@ class BaseXMPP(XMLStream):
"""
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
@fulljid.setter
def fulljid(self, value):
- logging.warning("fulljid property deprecated. Use boundjid.full")
+ log.warning("fulljid property deprecated. Use boundjid.full")
self.boundjid.full = value
@property
@@ -485,12 +479,12 @@ class BaseXMPP(XMLStream):
"""
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
@resource.setter
def resource(self, value):
- logging.warning("fulljid property deprecated. Use boundjid.full")
+ log.warning("fulljid property deprecated. Use boundjid.full")
self.boundjid.resource = value
@property
@@ -498,12 +492,12 @@ class BaseXMPP(XMLStream):
"""
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
@username.setter
def username(self, value):
- logging.warning("username property deprecated. Use boundjid.user")
+ log.warning("username property deprecated. Use boundjid.user")
self.boundjid.user = value
@property
@@ -511,17 +505,17 @@ class BaseXMPP(XMLStream):
"""
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
@server.setter
def server(self, value):
- logging.warning("server property deprecated. Use boundjid.host")
+ log.warning("server property deprecated. Use boundjid.host")
self.boundjid.server = value
def set_jid(self, jid):
"""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
def getjidresource(self, fulljid):
@@ -596,6 +590,5 @@ class BaseXMPP(XMLStream):
return
self.event("changed_status", presence)
-
# Restore the old, lowercased name for backwards compatibility.
basexmpp = BaseXMPP
diff --git a/sleekxmpp/clientxmpp.py b/sleekxmpp/clientxmpp.py
index 376fcf89..dbf87647 100644
--- a/sleekxmpp/clientxmpp.py
+++ b/sleekxmpp/clientxmpp.py
@@ -32,6 +32,9 @@ except:
SRV_SUPPORT = False
+log = logging.getLogger(__name__)
+
+
class ClientXMPP(BaseXMPP):
"""
@@ -132,7 +135,7 @@ class ClientXMPP(BaseXMPP):
def _session_timeout_check(self):
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)
def connect(self, address=tuple()):
@@ -149,19 +152,19 @@ class ClientXMPP(BaseXMPP):
self.session_started_event.clear()
if not address or len(address) < 2:
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" + \
" (http://www.dnspython.org)." + \
" Continuing to attempt connection, using" + \
" server hostname from JID.")
else:
- logging.debug("Since no address is supplied," + \
+ log.debug("Since no address is supplied," + \
"attempting SRV lookup.")
try:
xmpp_srv = "_xmpp-client._tcp.%s" % self.server
answers = dns.resolver.query(xmpp_srv, dns.rdatatype.SRV)
- except dns.resolver.NXDOMAIN:
- logging.debug("No appropriate SRV record found." + \
+ except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
+ log.debug("No appropriate SRV record found." + \
" Using JID server name.")
else:
# Pick a random server, weighted by priority.
@@ -275,7 +278,7 @@ class ClientXMPP(BaseXMPP):
self.send_xml(xml)
return True
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.")
return False
@@ -285,7 +288,7 @@ class ClientXMPP(BaseXMPP):
Restarts the stream.
"""
- logging.debug("Starting TLS")
+ log.debug("Starting TLS")
if self.start_tls():
raise RestartStream()
@@ -299,7 +302,7 @@ class ClientXMPP(BaseXMPP):
if '{urn:ietf:params:xml:ns:xmpp-tls}starttls' in self.features:
return False
- logging.debug("Starting SASL Auth")
+ log.debug("Starting SASL Auth")
sasl_ns = 'urn:ietf:params:xml:ns:xmpp-sasl'
self.add_handler("<success xmlns='%s' />" % sasl_ns,
self._handle_auth_success,
@@ -333,7 +336,7 @@ class ClientXMPP(BaseXMPP):
sasl_ns,
'ANONYMOUS'))
else:
- logging.error("No appropriate login method.")
+ log.error("No appropriate login method.")
self.disconnect()
return True
@@ -355,7 +358,7 @@ class ClientXMPP(BaseXMPP):
Arguments:
xml -- The SASL authentication failure element.
"""
- logging.info("Authentication failed.")
+ log.info("Authentication failed.")
self.event("failed_auth", direct=True)
self.disconnect()
@@ -366,7 +369,7 @@ class ClientXMPP(BaseXMPP):
Arguments:
xml -- The bind feature element.
"""
- logging.debug("Requesting resource: %s" % self.boundjid.resource)
+ log.debug("Requesting resource: %s" % self.boundjid.resource)
xml.clear()
iq = self.Iq(stype='set')
if self.boundjid.resource:
@@ -380,10 +383,10 @@ class ClientXMPP(BaseXMPP):
self.set_jid(response.xml.find('{%s}bind/{%s}jid' % (bind_ns,
bind_ns)).text)
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'
if "{%s}session" % session_ns not in self.features or self.bindfail:
- logging.debug("Established Session")
+ log.debug("Established Session")
self.sessionstarted = True
self.session_started_event.set()
self.event("session_start")
@@ -398,7 +401,7 @@ class ClientXMPP(BaseXMPP):
if self.authenticated and self.bound:
iq = self.makeIqSet(xml)
response = iq.send()
- logging.debug("Established Session")
+ log.debug("Established Session")
self.sessionstarted = True
self.session_started_event.set()
self.event("session_start")
diff --git a/sleekxmpp/componentxmpp.py b/sleekxmpp/componentxmpp.py
index ec3d1a04..0963c502 100644
--- a/sleekxmpp/componentxmpp.py
+++ b/sleekxmpp/componentxmpp.py
@@ -15,13 +15,16 @@ import hashlib
from sleekxmpp import plugins
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 StanzaBase, ET
from sleekxmpp.xmlstream.matcher import *
from sleekxmpp.xmlstream.handler import *
+log = logging.getLogger(__name__)
+
+
class ComponentXMPP(BaseXMPP):
"""
@@ -82,7 +85,7 @@ class ComponentXMPP(BaseXMPP):
Overrides XMLStream.connect.
"""
- logging.debug("Connecting to %s:%s" % (self.server_host,
+ log.debug("Connecting to %s:%s" % (self.server_host,
self.server_port))
return XMLStream.connect(self, self.server_host,
self.server_port)
diff --git a/sleekxmpp/plugins/__init__.py b/sleekxmpp/plugins/__init__.py
index 4dcec8f0..427ab04e 100644
--- a/sleekxmpp/plugins/__init__.py
+++ b/sleekxmpp/plugins/__init__.py
@@ -6,5 +6,5 @@
See the file LICENSE for copying permission.
"""
__all__ = ['xep_0004', 'xep_0012', 'xep_0030', 'xep_0033', 'xep_0045',
- 'xep_0050', 'xep_0078', 'xep_0085', 'xep_0092', 'xep_0199',
- 'gmail_notify', 'xep_0060', 'xep_0202']
+ 'xep_0050', 'xep_0085', 'xep_0092', 'xep_0199', 'gmail_notify',
+ 'xep_0060', 'xep_0202']
diff --git a/sleekxmpp/plugins/gmail_notify.py b/sleekxmpp/plugins/gmail_notify.py
index 7e442346..7e888b90 100644
--- a/sleekxmpp/plugins/gmail_notify.py
+++ b/sleekxmpp/plugins/gmail_notify.py
@@ -14,6 +14,9 @@ from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID
from .. stanza.iq import Iq
+log = logging.getLogger(__name__)
+
+
class GmailQuery(ElementBase):
namespace = 'google:mail:notify'
name = 'query'
@@ -34,12 +37,12 @@ class MailBox(ElementBase):
namespace = 'google:mail:notify'
name = 'mailbox'
plugin_attrib = 'mailbox'
- interfaces = set(('result-time', 'total-matched', 'total-estimate',
+ interfaces = set(('result-time', 'total-matched', 'total-estimate',
'url', 'threads', 'matched', 'estimate'))
def getThreads(self):
threads = []
- for threadXML in self.xml.findall('{%s}%s' % (MailThread.namespace,
+ for threadXML in self.xml.findall('{%s}%s' % (MailThread.namespace,
MailThread.name)):
threads.append(MailThread(xml=threadXML, parent=None))
return threads
@@ -55,10 +58,10 @@ class MailThread(ElementBase):
namespace = 'google:mail:notify'
name = 'mail-thread-info'
plugin_attrib = 'thread'
- interfaces = set(('tid', 'participation', 'messages', 'date',
+ interfaces = set(('tid', 'participation', 'messages', 'date',
'senders', 'url', 'labels', 'subject', 'snippet'))
sub_interfaces = set(('labels', 'subject', 'snippet'))
-
+
def getSenders(self):
senders = []
sendersXML = self.xml.find('{%s}senders' % self.namespace)
@@ -91,13 +94,13 @@ class gmail_notify(base.base_plugin):
"""
Google Talk: Gmail Notifications
"""
-
+
def plugin_init(self):
self.description = 'Google Talk: Gmail Notifications'
self.xmpp.registerHandler(
Callback('Gmail Result',
- MatchXPath('{%s}iq/{%s}%s' % (self.xmpp.default_ns,
+ MatchXPath('{%s}iq/{%s}%s' % (self.xmpp.default_ns,
MailBox.namespace,
MailBox.name)),
self.handle_gmail))
@@ -108,7 +111,7 @@ class gmail_notify(base.base_plugin):
NewMail.namespace,
NewMail.name)),
self.handle_new_mail))
-
+
registerStanzaPlugin(Iq, GmailQuery)
registerStanzaPlugin(Iq, MailBox)
registerStanzaPlugin(Iq, NewMail)
@@ -118,12 +121,12 @@ class gmail_notify(base.base_plugin):
def handle_gmail(self, iq):
mailbox = iq['mailbox']
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.xmpp.event('gmail_messages', 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.checkEmail()
@@ -135,9 +138,9 @@ class gmail_notify(base.base_plugin):
def search(self, query=None, newer=None):
if query is None:
- logging.info("Gmail: Checking for new emails")
+ log.info("Gmail: Checking for new emails")
else:
- logging.info('Gmail: Searching for emails matching: "%s"' % query)
+ log.info('Gmail: Searching for emails matching: "%s"' % query)
iq = self.xmpp.Iq()
iq['type'] = 'get'
iq['to'] = self.xmpp.jid
diff --git a/sleekxmpp/plugins/jobs.py b/sleekxmpp/plugins/jobs.py
index c52e524e..0b93d62e 100644
--- a/sleekxmpp/plugins/jobs.py
+++ b/sleekxmpp/plugins/jobs.py
@@ -3,15 +3,19 @@ import logging
from xml.etree import cElementTree as ET
import types
+
+log = logging.getLogger(__name__)
+
+
class jobs(base.base_plugin):
def plugin_init(self):
self.xep = 'pubsubjob'
self.description = "Job distribution over Pubsub"
-
+
def post_init(self):
pass
#TODO add event
-
+
def createJobNode(self, host, jid, node, config=None):
pass
@@ -40,7 +44,7 @@ class jobs(base.base_plugin):
iq['psstate']['payload'] = state
result = iq.send()
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 True
diff --git a/sleekxmpp/plugins/old_0004.py b/sleekxmpp/plugins/old_0004.py
index 651408ae..ade3d682 100644
--- a/sleekxmpp/plugins/old_0004.py
+++ b/sleekxmpp/plugins/old_0004.py
@@ -2,42 +2,46 @@
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
-
+
See the file LICENSE for copying permission.
"""
from . import base
-import logging
+import log
from xml.etree import cElementTree as ET
import copy
import logging
#TODO support item groups and results
+
+log = logging.getLogger(__name__)
+
+
class old_0004(base.base_plugin):
-
+
def plugin_init(self):
self.xep = '0004'
self.description = '*Deprecated Data Forms'
self.xmpp.add_handler("<message><x xmlns='jabber:x:data' /></message>", self.handler_message_xform, name='Old Message Form')
-
+
def post_init(self):
base.base_plugin.post_init(self)
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):
object = self.handle_form(xml)
self.xmpp.event("message_form", object)
-
+
def handler_presence_xform(self, xml):
object = self.handle_form(xml)
self.xmpp.event("presence_form", object)
-
+
def handle_form(self, xml):
xmlform = xml.find('{jabber:x:data}x')
object = self.buildForm(xmlform)
self.xmpp.event("message_xform", object)
return object
-
+
def buildForm(self, xml):
form = Form(ftype=xml.attrib['type'])
form.fromXML(xml)
@@ -51,12 +55,12 @@ class FieldContainer(object):
self.fields = []
self.field = {}
self.stanza = stanza
-
+
def addField(self, var, ftype='text-single', label='', desc='', required=False, value=None):
self.field[var] = FormField(var, ftype, label, desc, required, value)
self.fields.append(self.field[var])
return self.field[var]
-
+
def buildField(self, xml):
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__')])
@@ -66,13 +70,13 @@ class FieldContainer(object):
self.stanza = xml.tag
for field in xml.findall('{jabber:x:data}field'):
self.buildField(field)
-
+
def getXML(self, ftype):
container = ET.Element(self.stanza)
for field in self.fields:
container.append(field.getXML(ftype))
return container
-
+
class Form(FieldContainer):
types = ('form', 'submit', 'cancel', 'result')
def __init__(self, xmpp=None, ftype='form', title='', instructions=''):
@@ -85,7 +89,7 @@ class Form(FieldContainer):
self.instructions = instructions
self.reported = []
self.items = []
-
+
def merge(self, form2):
form1 = Form(ftype=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:
form1.fields[field.var].addOption(option, label)
return form1
-
+
def copy(self):
newform = Form(ftype=self.type)
newform.fromXML(self.getXML(self.type))
return newform
-
+
def update(self, form):
values = form.getValues()
for var in values:
if var in self.fields:
self.fields[var].setValue(self.fields[var])
-
+
def getValues(self):
result = {}
for field in self.fields:
@@ -118,7 +122,7 @@ class Form(FieldContainer):
value = value[0]
result[field.var] = value
return result
-
+
def setValues(self, values={}):
for field in values:
if field in self.field:
@@ -127,10 +131,10 @@ class Form(FieldContainer):
self.field[field].setValue(value)
else:
self.field[field].setValue(values[field])
-
+
def fromXML(self, xml):
self.buildForm(xml)
-
+
def addItem(self):
newitem = FieldContainer('item')
self.items.append(newitem)
@@ -148,21 +152,21 @@ class Form(FieldContainer):
def buildReported(self, xml):
reported = self.addReported()
reported.buildContainer(xml)
-
+
def setTitle(self, title):
self.title = title
-
+
def setInstructions(self, instructions):
self.instructions = instructions
-
+
def setType(self, ftype):
self.type = ftype
-
+
def getXMLMessage(self, to):
msg = self.xmpp.makeMessage(to)
msg.append(self.getXML())
return msg
-
+
def buildForm(self, xml):
self.type = xml.get('type', 'form')
if xml.find('{jabber:x:data}title') is not None:
@@ -175,7 +179,7 @@ class Form(FieldContainer):
self.buildReported(reported)
for item in xml.findall('{jabber:x:data}item'):
self.buildItem(item)
-
+
#def getXML(self, tostring = False):
def getXML(self, ftype=None):
if ftype:
@@ -199,7 +203,7 @@ class Form(FieldContainer):
#if tostring:
# form = self.xmpp.tostring(form)
return form
-
+
def getXHTML(self):
form = ET.Element('{http://www.w3.org/1999/xhtml}form')
if self.title:
@@ -217,8 +221,8 @@ class Form(FieldContainer):
for field in self.items:
form.append(field.getXHTML())
return form
-
-
+
+
def makeSubmit(self):
self.setType('submit')
@@ -246,13 +250,13 @@ class FormField(object):
self.islinebreak = False
if value:
self.setValue(value)
-
+
def addOption(self, value, label):
if self.islist:
self.options.append((value, label))
else:
raise ValueError("Cannot add options to non-list type field.")
-
+
def setTrue(self):
if self.type == 'boolean':
self.value = [True]
@@ -263,10 +267,10 @@ class FormField(object):
def require(self):
self.required = True
-
+
def setDescription(self, desc):
self.desc = desc
-
+
def setValue(self, value):
if self.type == 'boolean':
if value in ('1', 1, True, 'true', 'True', 'yes'):
@@ -291,10 +295,10 @@ class FormField(object):
pass
else:
self.value = ''
-
+
def setAnswer(self, value):
self.setValue(value)
-
+
def buildField(self, xml):
self.type = xml.get('type', 'text-single')
self.label = xml.get('label', '')
@@ -306,7 +310,7 @@ class FormField(object):
self.require()
if xml.find('{jabber:x:data}desc') is not None:
self.setDescription(xml.find('{jabber:x:data}desc').text)
-
+
def getXML(self, ftype):
field = ET.Element('{jabber:x:data}field')
if ftype != 'result':
@@ -342,7 +346,7 @@ class FormField(object):
valuexml.text = value
field.append(valuexml)
return field
-
+
def getXHTML(self):
field = ET.Element('div', {'class': 'xmpp-xforms-%s' % self.type})
if self.label:
@@ -414,4 +418,4 @@ class FormField(object):
pass
label.append(formf)
return field
-
+
diff --git a/sleekxmpp/plugins/xep_0004.py b/sleekxmpp/plugins/xep_0004.py
index e8dba74c..b8b7ebfa 100644
--- a/sleekxmpp/plugins/xep_0004.py
+++ b/sleekxmpp/plugins/xep_0004.py
@@ -16,6 +16,9 @@ from .. stanza.message import Message
import types
+log = logging.getLogger(__name__)
+
+
class Form(ElementBase):
namespace = 'jabber:x:data'
name = 'x'
@@ -33,7 +36,7 @@ class Form(ElementBase):
if title is not None:
self['title'] = title
self.field = FieldAccessor(self)
-
+
def setup(self, xml=None):
if ElementBase.setup(self, xml): #if we had to generate xml
self['type'] = 'form'
@@ -55,11 +58,11 @@ class Form(ElementBase):
return field
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
-
+
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)
return n
@@ -113,10 +116,10 @@ class Form(ElementBase):
reportedXML = self.xml.find('{%s}reported' % self.namespace)
if reportedXML is not None:
self.xml.remove(reportedXML)
-
+
def getFields(self, use_dict=False):
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:
field = FormField(xml=fieldXML)
if use_dict:
@@ -144,7 +147,7 @@ class Form(ElementBase):
def getReported(self):
fields = {}
- fieldsXML = self.xml.findall('{%s}reported/{%s}field' % (self.namespace,
+ fieldsXML = self.xml.findall('{%s}reported/{%s}field' % (self.namespace,
FormField.namespace))
for fieldXML in fieldsXML:
field = FormField(xml=fieldXML)
@@ -197,7 +200,7 @@ class Form(ElementBase):
fields = self.getFields(use_dict=True)
for field in values:
fields[field]['value'] = values[field]
-
+
def merge(self, other):
new = copy.copy(self)
if type(other) == types.DictType:
@@ -212,7 +215,7 @@ class Form(ElementBase):
class FieldAccessor(object):
def __init__(self, form):
self.form = form
-
+
def __getitem__(self, key):
return self.form.getFields(use_dict=True)[key]
@@ -366,21 +369,21 @@ class xep_0004(base.base_plugin):
self.xmpp.registerHandler(
Callback('Data Form',
- MatchXPath('{%s}message/{%s}x' % (self.xmpp.default_ns,
+ MatchXPath('{%s}message/{%s}x' % (self.xmpp.default_ns,
Form.namespace)),
self.handle_form))
registerStanzaPlugin(FormField, FieldOption)
registerStanzaPlugin(Form, FormField)
registerStanzaPlugin(Message, Form)
-
+
def makeForm(self, ftype='form', title='', instructions=''):
f = Form()
f['type'] = ftype
f['title'] = title
f['instructions'] = instructions
return f
-
+
def post_init(self):
base.base_plugin.post_init(self)
self.xmpp.plugin['xep_0030'].add_feature('jabber:x:data')
diff --git a/sleekxmpp/plugins/xep_0012.py b/sleekxmpp/plugins/xep_0012.py
index 45ca8a00..d636d4d7 100644
--- a/sleekxmpp/plugins/xep_0012.py
+++ b/sleekxmpp/plugins/xep_0012.py
@@ -16,6 +16,9 @@ from .. xmlstream.matcher.xpath import MatchXPath
from .. xmlstream import ElementBase, ET, JID, register_stanza_plugin
+log = logging.getLogger(__name__)
+
+
class LastActivity(ElementBase):
name = 'query'
namespace = 'jabber:iq:last'
@@ -68,10 +71,10 @@ class xep_0012(base.base_plugin):
def handle_last_activity_query(self, iq):
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)
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)
def handle_last_activity(self, iq):
diff --git a/sleekxmpp/plugins/xep_0030.py b/sleekxmpp/plugins/xep_0030.py
index a9d8d6a7..a3fac346 100644
--- a/sleekxmpp/plugins/xep_0030.py
+++ b/sleekxmpp/plugins/xep_0030.py
@@ -13,315 +13,317 @@ from .. xmlstream.matcher.xpath import MatchXPath
from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID
from .. stanza.iq import Iq
+
+log = logging.getLogger(__name__)
+
+
class DiscoInfo(ElementBase):
- namespace = 'http://jabber.org/protocol/disco#info'
- name = 'query'
- plugin_attrib = 'disco_info'
- interfaces = set(('node', 'features', 'identities'))
-
- def getFeatures(self):
- features = []
- featuresXML = self.xml.findall('{%s}feature' % self.namespace)
- for feature in featuresXML:
- features.append(feature.attrib['var'])
- return features
-
- def setFeatures(self, features):
- self.delFeatures()
- for name in features:
- self.addFeature(name)
-
- def delFeatures(self):
- featuresXML = self.xml.findall('{%s}feature' % self.namespace)
- for feature in featuresXML:
- self.xml.remove(feature)
-
- def addFeature(self, feature):
- featureXML = ET.Element('{%s}feature' % self.namespace,
- {'var': feature})
- self.xml.append(featureXML)
-
- def delFeature(self, feature):
- featuresXML = self.xml.findall('{%s}feature' % self.namespace)
- for featureXML in featuresXML:
- if featureXML.attrib['var'] == feature:
- self.xml.remove(featureXML)
-
- def getIdentities(self):
- ids = []
- idsXML = self.xml.findall('{%s}identity' % self.namespace)
- for idXML in idsXML:
- idData = (idXML.attrib['category'],
- idXML.attrib['type'],
- idXML.attrib.get('name', ''))
- ids.append(idData)
- return ids
-
- def setIdentities(self, ids):
- self.delIdentities()
- for idData in ids:
- self.addIdentity(*idData)
-
- def delIdentities(self):
- idsXML = self.xml.findall('{%s}identity' % self.namespace)
- for idXML in idsXML:
- self.xml.remove(idXML)
-
- def addIdentity(self, category, id_type, name=''):
- idXML = ET.Element('{%s}identity' % self.namespace,
- {'category': category,
- 'type': id_type,
- 'name': name})
- self.xml.append(idXML)
-
- def delIdentity(self, category, id_type, name=''):
- idsXML = self.xml.findall('{%s}identity' % self.namespace)
- for idXML in idsXML:
- idData = (idXML.attrib['category'],
- idXML.attrib['type'])
- delId = (category, id_type)
- if idData == delId:
- self.xml.remove(idXML)
+ namespace = 'http://jabber.org/protocol/disco#info'
+ name = 'query'
+ plugin_attrib = 'disco_info'
+ interfaces = set(('node', 'features', 'identities'))
+
+ def getFeatures(self):
+ features = []
+ featuresXML = self.xml.findall('{%s}feature' % self.namespace)
+ for feature in featuresXML:
+ features.append(feature.attrib['var'])
+ return features
+
+ def setFeatures(self, features):
+ self.delFeatures()
+ for name in features:
+ self.addFeature(name)
+
+ def delFeatures(self):
+ featuresXML = self.xml.findall('{%s}feature' % self.namespace)
+ for feature in featuresXML:
+ self.xml.remove(feature)
+
+ def addFeature(self, feature):
+ featureXML = ET.Element('{%s}feature' % self.namespace,
+ {'var': feature})
+ self.xml.append(featureXML)
+
+ def delFeature(self, feature):
+ featuresXML = self.xml.findall('{%s}feature' % self.namespace)
+ for featureXML in featuresXML:
+ if featureXML.attrib['var'] == feature:
+ self.xml.remove(featureXML)
+
+ def getIdentities(self):
+ ids = []
+ idsXML = self.xml.findall('{%s}identity' % self.namespace)
+ for idXML in idsXML:
+ idData = (idXML.attrib['category'],
+ idXML.attrib['type'],
+ idXML.attrib.get('name', ''))
+ ids.append(idData)
+ return ids
+
+ def setIdentities(self, ids):
+ self.delIdentities()
+ for idData in ids:
+ self.addIdentity(*idData)
+
+ def delIdentities(self):
+ idsXML = self.xml.findall('{%s}identity' % self.namespace)
+ for idXML in idsXML:
+ self.xml.remove(idXML)
+
+ def addIdentity(self, category, id_type, name=''):
+ idXML = ET.Element('{%s}identity' % self.namespace,
+ {'category': category,
+ 'type': id_type,
+ 'name': name})
+ self.xml.append(idXML)
+
+ def delIdentity(self, category, id_type, name=''):
+ idsXML = self.xml.findall('{%s}identity' % self.namespace)
+ for idXML in idsXML:
+ idData = (idXML.attrib['category'],
+ idXML.attrib['type'])
+ delId = (category, id_type)
+ if idData == delId:
+ self.xml.remove(idXML)
class DiscoItems(ElementBase):
- namespace = 'http://jabber.org/protocol/disco#items'
- name = 'query'
- plugin_attrib = 'disco_items'
- interfaces = set(('node', 'items'))
-
- def getItems(self):
- items = []
- itemsXML = self.xml.findall('{%s}item' % self.namespace)
- for item in itemsXML:
- itemData = (item.attrib['jid'],
- item.attrib.get('node'),
- item.attrib.get('name'))
- items.append(itemData)
- return items
-
- def setItems(self, items):
- self.delItems()
- for item in items:
- self.addItem(*item)
-
- def delItems(self):
- itemsXML = self.xml.findall('{%s}item' % self.namespace)
- for item in itemsXML:
- self.xml.remove(item)
-
- def addItem(self, jid, node='', name=''):
- itemXML = ET.Element('{%s}item' % self.namespace, {'jid': jid})
- if name:
- itemXML.attrib['name'] = name
- if node:
- itemXML.attrib['node'] = node
- 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)
-
+ namespace = 'http://jabber.org/protocol/disco#items'
+ name = 'query'
+ plugin_attrib = 'disco_items'
+ interfaces = set(('node', 'items'))
+
+ def getItems(self):
+ items = []
+ itemsXML = self.xml.findall('{%s}item' % self.namespace)
+ for item in itemsXML:
+ itemData = (item.attrib['jid'],
+ item.attrib.get('node'),
+ item.attrib.get('name'))
+ items.append(itemData)
+ return items
+
+ def setItems(self, items):
+ self.delItems()
+ for item in items:
+ self.addItem(*item)
+
+ def delItems(self):
+ itemsXML = self.xml.findall('{%s}item' % self.namespace)
+ for item in itemsXML:
+ self.xml.remove(item)
+
+ def addItem(self, jid, node='', name=''):
+ itemXML = ET.Element('{%s}item' % self.namespace, {'jid': jid})
+ if name:
+ itemXML.attrib['name'] = name
+ if node:
+ itemXML.attrib['node'] = node
+ 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)
+
class DiscoNode(object):
- """
- Collection object for grouping info and item information
- into nodes.
- """
- def __init__(self, name):
- self.name = name
- self.info = DiscoInfo()
- self.items = DiscoItems()
-
- self.info['node'] = name
- self.items['node'] = name
-
- # This is a bit like poor man's inheritance, but
- # to simplify adding information to the node we
- # map node functions to either the info or items
- # stanza objects.
- #
- # We don't want to make DiscoNode inherit from
- # DiscoInfo and DiscoItems because DiscoNode is
- # not an actual stanza, and doing so would create
- # confusion and potential bugs.
-
- self._map(self.items, 'items', ['get', 'set', 'del'])
- self._map(self.items, 'item', ['add', 'del'])
- self._map(self.info, 'identities', ['get', 'set', 'del'])
- self._map(self.info, 'identity', ['add', 'del'])
- self._map(self.info, 'features', ['get', 'set', 'del'])
- self._map(self.info, 'feature', ['add', 'del'])
-
- def isEmpty(self):
- """
- Test if the node contains any information. Useful for
- determining if a node can be deleted.
- """
- ids = self.getIdentities()
- features = self.getFeatures()
- items = self.getItems()
-
- if not ids and not features and not items:
- return True
- return False
-
- def _map(self, obj, interface, access):
- """
- Map functions of the form obj.accessInterface
- to self.accessInterface for each given access type.
- """
- interface = interface.title()
- for access_type in access:
- method = access_type + interface
- if hasattr(obj, method):
- setattr(self, method, getattr(obj, method))
+ """
+ Collection object for grouping info and item information
+ into nodes.
+ """
+ def __init__(self, name):
+ self.name = name
+ self.info = DiscoInfo()
+ self.items = DiscoItems()
+
+ self.info['node'] = name
+ self.items['node'] = name
+
+ # This is a bit like poor man's inheritance, but
+ # to simplify adding information to the node we
+ # map node functions to either the info or items
+ # stanza objects.
+ #
+ # We don't want to make DiscoNode inherit from
+ # DiscoInfo and DiscoItems because DiscoNode is
+ # not an actual stanza, and doing so would create
+ # confusion and potential bugs.
+
+ self._map(self.items, 'items', ['get', 'set', 'del'])
+ self._map(self.items, 'item', ['add', 'del'])
+ self._map(self.info, 'identities', ['get', 'set', 'del'])
+ self._map(self.info, 'identity', ['add', 'del'])
+ self._map(self.info, 'features', ['get', 'set', 'del'])
+ self._map(self.info, 'feature', ['add', 'del'])
+
+ def isEmpty(self):
+ """
+ Test if the node contains any information. Useful for
+ determining if a node can be deleted.
+ """
+ ids = self.getIdentities()
+ features = self.getFeatures()
+ items = self.getItems()
+
+ if not ids and not features and not items:
+ return True
+ return False
+
+ def _map(self, obj, interface, access):
+ """
+ Map functions of the form obj.accessInterface
+ to self.accessInterface for each given access type.
+ """
+ interface = interface.title()
+ for access_type in access:
+ method = access_type + interface
+ if hasattr(obj, method):
+ setattr(self, method, getattr(obj, method))
class xep_0030(base.base_plugin):
- """
- XEP-0030 Service Discovery
- """
-
- def plugin_init(self):
- self.xep = '0030'
- self.description = 'Service Discovery'
-
- self.xmpp.registerHandler(
- Callback('Disco Items',
- MatchXPath('{%s}iq/{%s}query' % (self.xmpp.default_ns,
- DiscoItems.namespace)),
- self.handle_item_query))
-
- self.xmpp.registerHandler(
- Callback('Disco Info',
- MatchXPath('{%s}iq/{%s}query' % (self.xmpp.default_ns,
- DiscoInfo.namespace)),
- self.handle_info_query))
-
- registerStanzaPlugin(Iq, DiscoInfo)
- registerStanzaPlugin(Iq, DiscoItems)
-
- self.xmpp.add_event_handler('disco_items_request', self.handle_disco_items)
- self.xmpp.add_event_handler('disco_info_request', self.handle_disco_info)
-
- self.nodes = {'main': DiscoNode('main')}
-
- def add_node(self, node):
- if node not in self.nodes:
- self.nodes[node] = DiscoNode(node)
-
- def del_node(self, node):
- if node in self.nodes:
- del self.nodes[node]
-
- def handle_item_query(self, iq):
- if iq['type'] == 'get':
- logging.debug("Items requested by %s" % iq['from'])
- 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):
- if iq['type'] == 'get':
- logging.debug("Info requested by %s" % iq['from'])
- self.xmpp.event('disco_info_request', iq)
- elif iq['type'] == 'result':
- logging.debug("Info result from %s" % iq['from'])
- self.xmpp.event('disco_info', iq)
-
- def handle_disco_info(self, iq, forwarded=False):
- """
- A default handler for disco#info requests. If another
- handler is registered, this one will defer and not run.
- """
- handlers = self.xmpp.event_handlers['disco_info_request']
- if not forwarded and len(handlers) > 1:
- return
-
- node_name = iq['disco_info']['node']
- if not node_name:
- node_name = 'main'
-
- logging.debug("Using default handler for disco#info on node '%s'." % node_name)
-
- if node_name in self.nodes:
- 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
- forwarded set to True, then it will run as normal.
- """
- handlers = self.xmpp.event_handlers['disco_items_request']
- if not forwarded and len(handlers) > 1:
- return
-
- node_name = iq['disco_items']['node']
- if not node_name:
- node_name = 'main'
-
- logging.debug("Using default handler for disco#items on node '%s'." % node_name)
-
- if node_name in self.nodes:
- node = self.nodes[node_name]
- iq.reply().setPayload(node.items.xml).send()
- 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
-
- def getInfo(self, jid, node='', dfrom=None):
- iq = self.xmpp.Iq()
- iq['type'] = 'get'
- iq['to'] = jid
- iq['from'] = dfrom
- iq['disco_info']['node'] = node
- return iq.send()
-
- def getItems(self, jid, node='', dfrom=None):
- iq = self.xmpp.Iq()
- iq['type'] = 'get'
- iq['to'] = jid
- iq['from'] = dfrom
- iq['disco_items']['node'] = node
- return iq.send()
-
- def add_feature(self, feature, node='main'):
- self.add_node(node)
- self.nodes[node].addFeature(feature)
-
- def add_identity(self, category='', itype='', name='', node='main'):
- 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)
+ """
+ XEP-0030 Service Discovery
+ """
+
+ def plugin_init(self):
+ self.xep = '0030'
+ self.description = 'Service Discovery'
+
+ self.xmpp.registerHandler(
+ Callback('Disco Items',
+ MatchXPath('{%s}iq/{%s}query' % (self.xmpp.default_ns,
+ DiscoItems.namespace)),
+ self.handle_item_query))
+
+ self.xmpp.registerHandler(
+ Callback('Disco Info',
+ MatchXPath('{%s}iq/{%s}query' % (self.xmpp.default_ns,
+ DiscoInfo.namespace)),
+ self.handle_info_query))
+
+ registerStanzaPlugin(Iq, DiscoInfo)
+ registerStanzaPlugin(Iq, DiscoItems)
+
+ self.xmpp.add_event_handler('disco_items_request', self.handle_disco_items)
+ self.xmpp.add_event_handler('disco_info_request', self.handle_disco_info)
+
+ self.nodes = {'main': DiscoNode('main')}
+
+ def add_node(self, node):
+ if node not in self.nodes:
+ self.nodes[node] = DiscoNode(node)
+
+ def del_node(self, node):
+ if node in self.nodes:
+ del self.nodes[node]
+
+ def handle_item_query(self, iq):
+ if iq['type'] == 'get':
+ log.debug("Items requested by %s" % iq['from'])
+ self.xmpp.event('disco_items_request', iq)
+ elif iq['type'] == 'result':
+ log.debug("Items result from %s" % iq['from'])
+ self.xmpp.event('disco_items', iq)
+
+ def handle_info_query(self, iq):
+ if iq['type'] == 'get':
+ log.debug("Info requested by %s" % iq['from'])
+ self.xmpp.event('disco_info_request', iq)
+ elif iq['type'] == 'result':
+ log.debug("Info result from %s" % iq['from'])
+ self.xmpp.event('disco_info', iq)
+
+ def handle_disco_info(self, iq, forwarded=False):
+ """
+ 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
+
+ node_name = iq['disco_info']['node']
+ if not node_name:
+ node_name = 'main'
+
+ log.debug("Using default handler for disco#info on node '%s'." % node_name)
+
+ if node_name in self.nodes:
+ node = self.nodes[node_name]
+ iq.reply().setPayload(node.info.xml).send()
+ else:
+ log.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
+ forwarded set to True, then it will run as normal.
+ """
+ if not forwarded and self.xmpp.event_handled('disco_items_request'):
+ return
+
+ node_name = iq['disco_items']['node']
+ if not node_name:
+ node_name = 'main'
+
+ log.debug("Using default handler for disco#items on node '%s'." % node_name)
+
+ if node_name in self.nodes:
+ node = self.nodes[node_name]
+ iq.reply().setPayload(node.items.xml).send()
+ else:
+ log.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
+
+ def getInfo(self, jid, node='', dfrom=None):
+ iq = self.xmpp.Iq()
+ iq['type'] = 'get'
+ iq['to'] = jid
+ iq['from'] = dfrom
+ iq['disco_info']['node'] = node
+ return iq.send()
+
+ def getItems(self, jid, node='', dfrom=None):
+ iq = self.xmpp.Iq()
+ iq['type'] = 'get'
+ iq['to'] = jid
+ iq['from'] = dfrom
+ iq['disco_items']['node'] = node
+ return iq.send()
+
+ def add_feature(self, feature, node='main'):
+ self.add_node(node)
+ self.nodes[node].addFeature(feature)
+
+ def add_identity(self, category='', itype='', name='', node='main'):
+ 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)
diff --git a/sleekxmpp/plugins/xep_0045.py b/sleekxmpp/plugins/xep_0045.py
index bf472a46..db41cdb3 100644
--- a/sleekxmpp/plugins/xep_0045.py
+++ b/sleekxmpp/plugins/xep_0045.py
@@ -2,7 +2,7 @@
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
-
+
See the file LICENSE for copying permission.
"""
from __future__ import with_statement
@@ -15,6 +15,10 @@ from .. xmlstream.handler.callback import Callback
from .. xmlstream.matcher.xpath import MatchXPath
from .. xmlstream.matcher.xmlmask import MatchXMLMask
+
+log = logging.getLogger(__name__)
+
+
class MUCPresence(ElementBase):
name = 'x'
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
item = self.getXMLItem()
return item.get('affiliation', '')
-
+
def setAffiliation(self, value):
item = self.getXMLItem()
#TODO check for valid affiliation
item.attrib['affiliation'] = value
return self
-
+
def delAffiliation(self):
item = self.getXMLItem()
#TODO set default affiliation
if 'affiliation' in item.attrib: del item.attrib['affiliation']
return self
-
+
def getJid(self):
item = self.getXMLItem()
return JID(item.get('jid', ''))
-
+
def setJid(self, value):
item = self.getXMLItem()
if not isinstance(value, str):
value = str(value)
item.attrib['jid'] = value
return self
-
+
def delJid(self):
item = self.getXMLItem()
if 'jid' in item.attrib: del item.attrib['jid']
return self
-
+
def getRole(self):
item = self.getXMLItem()
#TODO get default role, set default role if none
return item.get('role', '')
-
+
def setRole(self, value):
item = self.getXMLItem()
#TODO check for valid role
item.attrib['role'] = value
return self
-
+
def delRole(self):
item = self.getXMLItem()
#TODO set default role
if 'role' in item.attrib: del item.attrib['role']
return self
-
+
def getNick(self):
return self.parent()['from'].resource
-
+
def getRoom(self):
return self.parent()['from'].bare
-
+
def setNick(self, value):
- logging.warning("Cannot set nick through mucpresence plugin.")
+ log.warning("Cannot set nick through mucpresence plugin.")
return self
-
+
def setRoom(self, value):
- logging.warning("Cannot set room through mucpresence plugin.")
+ log.warning("Cannot set room through mucpresence plugin.")
return self
-
+
def delNick(self):
- logging.warning("Cannot delete nick through mucpresence plugin.")
+ log.warning("Cannot delete nick through mucpresence plugin.")
return self
-
+
def delRoom(self):
- logging.warning("Cannot delete room through mucpresence plugin.")
+ log.warning("Cannot delete room through mucpresence plugin.")
return self
class xep_0045(base.base_plugin):
"""
Impliments XEP-0045 Multi User Chat
"""
-
+
def plugin_init(self):
self.rooms = {}
self.ourNicks = {}
@@ -116,7 +120,8 @@ class xep_0045(base.base_plugin):
registerStanzaPlugin(Presence, MUCPresence)
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('MUCSubject', MatchXMLMask("<message xmlns='%s' type='groupchat'><subject/></message>" % self.xmpp.default_ns), self.handle_groupchat_subject))
+
def handle_groupchat_presence(self, pr):
""" 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']]:
got_online = True
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("muc::%s::presence" % entry['room'], pr)
if got_offline:
self.xmpp.event("muc::%s::got_offline" % entry['room'], pr)
if got_online:
self.xmpp.event("muc::%s::got_online" % entry['room'], pr)
-
+
def handle_groupchat_message(self, msg):
""" Handle a message event in a muc.
"""
self.xmpp.event('groupchat_message', 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):
for nick in self.rooms[room]:
entry = self.rooms[room][nick]
if entry is not None and entry['jid'].full == jid:
return True
return False
-
+
def getNick(self, room, jid):
for nick in self.rooms[room]:
entry = self.rooms[room][nick]
@@ -176,12 +187,12 @@ class xep_0045(base.base_plugin):
if xform is None: return False
form = self.xmpp.plugin['old_0004'].buildForm(xform)
return form
-
+
def configureRoom(self, room, form=None, ifrom=None):
if form is None:
form = self.getRoomForm(room, ifrom=ifrom)
#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['to'] = room
if ifrom is not None:
@@ -194,7 +205,7 @@ class xep_0045(base.base_plugin):
if result['type'] == 'error':
return False
return True
-
+
def joinMUC(self, room, nick, maxhistory="0", password='', wait=False, pstatus=None, pshow=None):
""" 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.rooms[room] = {}
self.ourNicks[room] = nick
-
+
def destroy(self, room, reason='', altroom = '', ifrom=None):
iq = self.xmpp.makeIqSet()
if ifrom is not None:
@@ -246,9 +257,9 @@ class xep_0045(base.base_plugin):
raise TypeError
query = ET.Element('{http://jabber.org/protocol/muc#admin}query')
if nick is not None:
- item = ET.Element('item', {'affiliation':affiliation, 'nick':nick})
+ item = ET.Element('item', {'affiliation':affiliation, 'nick':nick})
else:
- item = ET.Element('item', {'affiliation':affiliation, 'jid':jid})
+ item = ET.Element('item', {'affiliation':affiliation, 'jid':jid})
query.append(item)
iq = self.xmpp.makeIqSet(query)
iq['to'] = room
@@ -256,7 +267,7 @@ class xep_0045(base.base_plugin):
if result is False or result['type'] != 'result':
raise ValueError
return True
-
+
def invite(self, room, jid, reason=''):
""" Invite a jid to a room."""
msg = self.xmpp.makeMessage(room)
@@ -279,7 +290,7 @@ class xep_0045(base.base_plugin):
else:
self.xmpp.sendPresence(pshow='unavailable', pto="%s/%s" % (room, nick))
del self.rooms[room]
-
+
def getRoomConfig(self, room):
iq = self.xmpp.makeIqGet('http://jabber.org/protocol/muc#owner')
iq['to'] = room
@@ -291,14 +302,14 @@ class xep_0045(base.base_plugin):
if form is None:
raise ValueError
return self.xmpp.plugin['xep_0004'].buildForm(form)
-
+
def cancelConfig(self, room):
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
x = ET.Element('{jabber:x:data}x', type='cancel')
query.append(x)
iq = self.xmpp.makeIqSet(query)
iq.send()
-
+
def setRoomConfig(self, room, config):
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
x = config.getXML('submit')
@@ -307,15 +318,15 @@ class xep_0045(base.base_plugin):
iq['to'] = room
iq['from'] = self.xmpp.jid
iq.send()
-
+
def getJoinedRooms(self):
return self.rooms.keys()
-
+
def getOurJidInRoom(self, roomJid):
""" Return the jid we're using in a room.
"""
return "%s/%s" % (roomJid, self.ourNicks[roomJid])
-
+
def getJidProperty(self, room, nick, jidProperty):
""" Get the property of a nick in a room, such as its 'jid' or 'affiliation'
If not found, return None.
@@ -324,7 +335,7 @@ class xep_0045(base.base_plugin):
return self.rooms[room][nick][jidProperty]
else:
return None
-
+
def getRoster(self, room):
""" Get the list of nicks in a room.
"""
diff --git a/sleekxmpp/plugins/xep_0060.py b/sleekxmpp/plugins/xep_0060.py
index 0b056f0b..a7c6d023 100644
--- a/sleekxmpp/plugins/xep_0060.py
+++ b/sleekxmpp/plugins/xep_0060.py
@@ -6,6 +6,10 @@ from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET
from . import stanza_pubsub
from . xep_0004 import Form
+
+log = logging.getLogger(__name__)
+
+
class xep_0060(base.base_plugin):
"""
XEP-0060 Publish Subscribe
@@ -14,7 +18,7 @@ class xep_0060(base.base_plugin):
def plugin_init(self):
self.xep = '0060'
self.description = 'Publish-Subscribe'
-
+
def create_node(self, jid, node, config=None, collection=False, ntype=None):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub')
create = ET.Element('create')
@@ -52,7 +56,7 @@ class xep_0060(base.base_plugin):
result = iq.send()
if result is False or result is None or result['type'] == 'error': return False
return True
-
+
def subscribe(self, jid, node, bare=True, subscribee=None):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub')
subscribe = ET.Element('subscribe')
@@ -72,7 +76,7 @@ class xep_0060(base.base_plugin):
result = iq.send()
if result is False or result is None or result['type'] == 'error': return False
return True
-
+
def unsubscribe(self, jid, node, bare=True, subscribee=None):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub')
unsubscribe = ET.Element('unsubscribe')
@@ -92,7 +96,7 @@ class xep_0060(base.base_plugin):
result = iq.send()
if result is False or result is None or result['type'] == 'error': return False
return True
-
+
def getNodeConfig(self, jid, node=None): # if no node, then grab default
pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub')
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)
result = iq.send()
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
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')
else:
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:
- logging.error("No form found.")
+ log.error("No form found.")
return False
return Form(xml=form)
-
+
def getNodeSubscriptions(self, jid, node):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub')
subscriptions = ET.Element('subscriptions')
@@ -133,7 +137,7 @@ class xep_0060(base.base_plugin):
id = iq['id']
result = iq.send()
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
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')
@@ -156,7 +160,7 @@ class xep_0060(base.base_plugin):
id = iq['id']
result = iq.send()
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
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')
@@ -181,8 +185,8 @@ class xep_0060(base.base_plugin):
return True
else:
return False
-
-
+
+
def setNodeConfig(self, jid, node, config):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub')
configure = ET.Element('configure')
@@ -195,10 +199,10 @@ class xep_0060(base.base_plugin):
iq.attrib['from'] = self.xmpp.fulljid
id = iq['id']
result = iq.send()
- if result is None or result['type'] == 'error':
+ if result is None or result['type'] == 'error':
return False
return True
-
+
def setItem(self, jid, node, items=[]):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub')
publish = ET.Element('publish')
@@ -218,7 +222,7 @@ class xep_0060(base.base_plugin):
result = iq.send()
if result is None or result is False or result['type'] == 'error': return False
return True
-
+
def addItem(self, jid, node, items=[]):
return self.setItem(jid, node, items)
@@ -237,7 +241,7 @@ class xep_0060(base.base_plugin):
result = iq.send()
if result is None or result is False or result['type'] == 'error': return False
return True
-
+
def getNodes(self, 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')
@@ -246,7 +250,7 @@ class xep_0060(base.base_plugin):
for item in items:
nodes[item.get('node')] = item.get('name')
return nodes
-
+
def getItems(self, 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')
@@ -264,7 +268,7 @@ class xep_0060(base.base_plugin):
try:
config.field['pubsub#collection'].setValue(parent)
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)
if not self.setNodeConfig(jid, child, config):
return False
@@ -298,7 +302,7 @@ class xep_0060(base.base_plugin):
try:
config.field['pubsub#collection'].setValue(parent)
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)
if not self.setNodeConfig(jid, child, config):
return False
diff --git a/sleekxmpp/plugins/xep_0078.py b/sleekxmpp/plugins/xep_0078.py
index 4b3ab829..d2c81b16 100644
--- a/sleekxmpp/plugins/xep_0078.py
+++ b/sleekxmpp/plugins/xep_0078.py
@@ -2,7 +2,7 @@
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
-
+
See the file LICENSE for copying permission.
"""
from __future__ import with_statement
@@ -12,6 +12,9 @@ import hashlib
from . import base
+log = logging.getLogger(__name__)
+
+
class xep_0078(base.base_plugin):
"""
XEP-0078 NON-SASL Authentication
@@ -23,14 +26,14 @@ class xep_0078(base.base_plugin):
#disabling until I fix conflict with PLAIN
#self.xmpp.registerFeature("<auth xmlns='http://jabber.org/features/iq-auth'/>", self.auth)
self.streamid = ''
-
+
def check_stream(self, xml):
self.streamid = xml.attrib['id']
if xml.get('version', '0') != '1.0':
self.auth()
-
+
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_query = ET.Element('{jabber:iq:auth}query')
auth_request.attrib['to'] = self.xmpp.server
@@ -47,12 +50,12 @@ class xep_0078(base.base_plugin):
query.append(username)
query.append(resource)
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.text = self.xmpp.password
query.append(password)
else:
- logging.debug("Authenticating via jabber:iq:auth Digest")
+ log.debug("Authenticating via jabber:iq:auth Digest")
digest = ET.Element('digest')
digest.text = hashlib.sha1(b"%s%s" % (self.streamid, self.xmpp.password)).hexdigest()
query.append(digest)
@@ -64,6 +67,6 @@ class xep_0078(base.base_plugin):
self.xmpp.sessionstarted = True
self.xmpp.event("session_start")
else:
- logging.info("Authentication failed")
+ log.info("Authentication failed")
self.xmpp.disconnect()
self.xmpp.event("failed_auth")
diff --git a/sleekxmpp/plugins/xep_0085.py b/sleekxmpp/plugins/xep_0085.py
index b7b5d6dd..3627e718 100644
--- a/sleekxmpp/plugins/xep_0085.py
+++ b/sleekxmpp/plugins/xep_0085.py
@@ -14,15 +14,18 @@ from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID
from .. stanza.message import Message
+log = logging.getLogger(__name__)
+
+
class ChatState(ElementBase):
namespace = 'http://jabber.org/protocol/chatstates'
plugin_attrib = 'chat_state'
interface = set(('state',))
states = set(('active', 'composing', 'gone', 'inactive', 'paused'))
-
+
def active(self):
self.setState('active')
-
+
def composing(self):
self.setState('composing')
@@ -67,11 +70,11 @@ class xep_0085(base.base_plugin):
"""
XEP-0085 Chat State Notifications
"""
-
+
def plugin_init(self):
self.xep = '0085'
self.description = 'Chat State Notifications'
-
+
handlers = [('Active Chat State', 'active'),
('Composing Chat State', 'composing'),
('Gone Chat State', 'gone'),
@@ -79,10 +82,10 @@ class xep_0085(base.base_plugin):
('Paused Chat State', 'paused')]
for handler in handlers:
self.xmpp.registerHandler(
- Callback(handler[0],
- MatchXPath("{%s}message/{%s}%s" % (self.xmpp.default_ns,
+ Callback(handler[0],
+ MatchXPath("{%s}message/{%s}%s" % (self.xmpp.default_ns,
ChatState.namespace,
- handler[1])),
+ handler[1])),
self._handleChatState))
registerStanzaPlugin(Message, Active)
@@ -90,12 +93,12 @@ class xep_0085(base.base_plugin):
registerStanzaPlugin(Message, Gone)
registerStanzaPlugin(Message, Inactive)
registerStanzaPlugin(Message, Paused)
-
+
def post_init(self):
base.base_plugin.post_init(self)
self.xmpp.plugin['xep_0030'].add_feature('http://jabber.org/protocol/chatstates')
-
+
def _handleChatState(self, msg):
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)
diff --git a/sleekxmpp/plugins/xep_0199.py b/sleekxmpp/plugins/xep_0199.py
index 3fc62f55..2e99ae76 100644
--- a/sleekxmpp/plugins/xep_0199.py
+++ b/sleekxmpp/plugins/xep_0199.py
@@ -2,7 +2,7 @@
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz
This file is part of SleekXMPP.
-
+
See the file LICENSE for copying permission.
"""
from xml.etree import cElementTree as ET
@@ -10,50 +10,54 @@ from . import base
import time
import logging
+
+log = logging.getLogger(__name__)
+
+
class xep_0199(base.base_plugin):
- """XEP-0199 XMPP Ping"""
-
- def plugin_init(self):
- self.description = "XMPP Ping"
- 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.running = False
- #if self.config.get('keepalive', 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):
- """ 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('{http://www.xmpp.org/extensions/xep-0199.html#ns}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
+ """XEP-0199 XMPP Ping"""
+
+ def plugin_init(self):
+ self.description = "XMPP Ping"
+ self.xep = "0199"
+ 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')
+ if self.config.get('keepalive', 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('urn:xmpp:ping')
+
+ def handler_pingserver(self, xml):
+ self.xmpp.schedule("xep-0119 ping", float(self.config.get('frequency', 300)), self.scheduled_ping, repeat=True)
+
+ def scheduled_ping(self):
+ log.debug("pinging...")
+ if self.sendPing(self.xmpp.server, self.config.get('timeout', 30)) is False:
+ log.debug("Did not recieve ping back in time. Requesting Reconnect.")
+ self.xmpp.reconnect()
+
+ def handler_ping(self, xml):
+ iq = self.xmpp.makeIqResult(xml.get('id', 'unknown'))
+ iq.attrib['to'] = xml.get('from', self.xmpp.boundjid.domain)
+ self.xmpp.send(iq)
+
+ 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
diff --git a/sleekxmpp/plugins/xep_0202.py b/sleekxmpp/plugins/xep_0202.py
index c3f81b2e..fe1191ea 100644
--- a/sleekxmpp/plugins/xep_0202.py
+++ b/sleekxmpp/plugins/xep_0202.py
@@ -17,6 +17,9 @@ from .. xmlstream.matcher.xpath import MatchXPath
from .. xmlstream import ElementBase, ET, JID, register_stanza_plugin
+log = logging.getLogger(__name__)
+
+
class EntityTime(ElementBase):
name = 'time'
namespace = 'urn:xmpp:time'
@@ -84,10 +87,10 @@ class xep_0202(base.base_plugin):
def handle_entity_time_query(self, iq):
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)
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)
def handle_entity_time(self, iq):
diff --git a/sleekxmpp/roster.py b/sleekxmpp/roster.py
index 48f52de1..da485336 100644
--- a/sleekxmpp/roster.py
+++ b/sleekxmpp/roster.py
@@ -442,7 +442,6 @@ class RosterItem(object):
key -- The state field to modify.
value -- The new value of the state field.
"""
- print "%s: %s" % (key, value)
if key in self._state:
if key in ['name', 'subscription', 'groups']:
self._state[key] = value
@@ -465,7 +464,7 @@ class RosterItem(object):
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.
"""
if self['to']:
diff --git a/sleekxmpp/stanza/rootstanza.py b/sleekxmpp/stanza/rootstanza.py
index 2677ea91..6975c72a 100644
--- a/sleekxmpp/stanza/rootstanza.py
+++ b/sleekxmpp/stanza/rootstanza.py
@@ -15,6 +15,9 @@ from sleekxmpp.stanza import Error
from sleekxmpp.xmlstream import ET, StanzaBase, register_stanza_plugin
+log = logging.getLogger(__name__)
+
+
class RootStanza(StanzaBase):
"""
@@ -58,7 +61,7 @@ class RootStanza(StanzaBase):
self['error']['text'] = "SleekXMPP got into trouble."
else:
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.send()
diff --git a/sleekxmpp/test/sleektest.py b/sleekxmpp/test/sleektest.py
index 4cc913a0..e49b5fea 100644
--- a/sleekxmpp/test/sleektest.py
+++ b/sleekxmpp/test/sleektest.py
@@ -1,5 +1,4 @@
"""
-
SleekXMPP: The Sleek XMPP Library
Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
This file is part of SleekXMPP.
@@ -27,27 +26,29 @@ class SleekTest(unittest.TestCase):
Message -- Create a Message stanza object.
Iq -- Create an Iq stanza object.
Presence -- Create a Presence stanza object.
- check_stanza -- Compare a generic stanza against an XML string.
- check_message -- Compare a Message 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.
+ check_jid -- Check a JID and its component parts.
+ check -- Compare a stanza against an XML string.
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.
+ 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.
compare -- Compare XML objects against each other.
"""
+ def runTest(self):
+ pass
+
def parse_xml(self, xml_string):
try:
xml = ET.fromstring(xml_string)
@@ -103,10 +104,8 @@ class SleekTest(unittest.TestCase):
"""
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.
@@ -142,7 +141,6 @@ class SleekTest(unittest.TestCase):
afrom=None, ato=None, pending_out=None, pending_in=None,
groups=None):
roster = self.xmpp.roster[owner][jid]
- print roster._state
if name is not None:
self.assertEqual(roster['name'], name,
"Incorrect name value: %s" % roster['name'])
@@ -168,8 +166,8 @@ class SleekTest(unittest.TestCase):
# ------------------------------------------------------------------
# Methods for comparing stanza objects to XML strings
- def check_stanza(self, stanza_class, stanza, xml_string,
- defaults=None, use_values=True):
+ def check(self, stanza, xml_string,
+ defaults=None, use_values=True):
"""
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.
Arguments:
- stanza_class -- The class of the stanza being tested.
stanza -- The stanza object to test.
xml_string -- A string version of the correct XML expected.
defaults -- A list of stanza interfaces that have default
@@ -199,6 +196,7 @@ class SleekTest(unittest.TestCase):
setStanzaValues() should be used. Defaults to
True.
"""
+ stanza_class = stanza.__class__
xml = self.parse_xml(xml_string)
# Ensure that top level namespaces are used, even if they
@@ -215,7 +213,11 @@ class SleekTest(unittest.TestCase):
# so that they will compare correctly.
default_stanza = stanza_class()
if defaults is None:
- defaults = []
+ known_defaults = {
+ Message: ['type'],
+ Presence: ['priority']
+ }
+ defaults = known_defaults.get(stanza_class, [])
for interface in defaults:
stanza[interface] = stanza[interface]
stanza2[interface] = stanza2[interface]
@@ -246,62 +248,6 @@ class SleekTest(unittest.TestCase):
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.
@@ -329,7 +275,6 @@ class SleekTest(unittest.TestCase):
port -- The port to use when connecting to the server.
Defaults to 5222.
"""
-
if mode == 'client':
self.xmpp = ClientXMPP(jid, password)
elif mode == 'component':
@@ -364,13 +309,13 @@ class SleekTest(unittest.TestCase):
if mode == 'component':
self.xmpp.socket.next_sent(timeout=1)
- def stream_make_header(self, sto='',
- sfrom='',
- sid='',
- stream_ns="http://etherx.jabber.org/streams",
- default_ns="jabber:client",
- version="1.0",
- xml_header=True):
+ def make_header(self, sto='',
+ sfrom='',
+ sid='',
+ stream_ns="http://etherx.jabber.org/streams",
+ default_ns="jabber:client",
+ version="1.0",
+ xml_header=True):
"""
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)
return header % ' '.join(parts)
- def stream_recv(self, data, stanza_class=StanzaBase, defaults=[],
- use_values=True, timeout=1):
+ def recv(self, data, stanza_class=StanzaBase, defaults=[],
+ use_values=True, timeout=1):
"""
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:
return False
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,
use_values=use_values)
else:
@@ -437,14 +382,14 @@ class SleekTest(unittest.TestCase):
data = str(data)
self.xmpp.socket.recv_data(data)
- def stream_recv_header(self, sto='',
- sfrom='',
- sid='',
- stream_ns="http://etherx.jabber.org/streams",
- default_ns="jabber:client",
- version="1.0",
- xml_header=False,
- timeout=1):
+ def recv_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 received.
@@ -460,11 +405,11 @@ class SleekTest(unittest.TestCase):
timeout -- Length of time to wait in seconds for a
response.
"""
- header = self.stream_make_header(sto, sfrom, sid,
- stream_ns=stream_ns,
- default_ns=default_ns,
- version=version,
- xml_header=xml_header)
+ header = self.make_header(sto, sfrom, sid,
+ stream_ns=stream_ns,
+ default_ns=default_ns,
+ version=version,
+ xml_header=xml_header)
recv_header = self.xmpp.socket.next_recv(timeout)
if recv_header is None:
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" % (
'%s %s' % (xml.tag, 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:
@@ -526,39 +470,14 @@ class SleekTest(unittest.TestCase):
data = str(data)
self.xmpp.socket.recv_data(data)
-
-
- def stream_recv_message(self, data, use_values=True, timeout=1):
- """
- """
- return self.stream_recv(data, stanza_class=Message,
- defaults=['type'],
- use_values=use_values,
- 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):
+ def 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.
@@ -574,11 +493,11 @@ class SleekTest(unittest.TestCase):
timeout -- Length of time to wait in seconds for a
response.
"""
- header = self.stream_make_header(sto, sfrom, sid,
- stream_ns=stream_ns,
- default_ns=default_ns,
- version=version,
- xml_header=xml_header)
+ header = self.make_header(sto, sfrom, sid,
+ stream_ns=stream_ns,
+ default_ns=default_ns,
+ version=version,
+ xml_header=xml_header)
sent_header = self.xmpp.socket.next_sent(timeout)
if sent_header is None:
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" % (
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)
@@ -608,13 +527,13 @@ class SleekTest(unittest.TestCase):
"Features do not match.\nDesired:\n%s\nSent:\n%s" % (
tostring(xml), tostring(sent_xml)))
- def stream_send_stanza(self, stanza_class, data, defaults=None,
- use_values=True, timeout=.1):
+ def send(self, data, defaults=None,
+ 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_stanza.
+ XML using check.
Arguments:
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
failing the check.
"""
- if isintance(data, str):
- data = stanza_class(xml=self.parse_xml(data))
+ if isinstance(data, str):
+ 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)
- self.check_stanza(stanza_class, data, sent,
+ self.check(data, sent,
defaults=defaults,
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):
"""
Disconnect the dummy XMPP client.
diff --git a/sleekxmpp/thirdparty/statemachine.py b/sleekxmpp/thirdparty/statemachine.py
index b176df02..8a7324b5 100644
--- a/sleekxmpp/thirdparty/statemachine.py
+++ b/sleekxmpp/thirdparty/statemachine.py
@@ -21,7 +21,7 @@ class StateMachine(object):
self.addStates(states)
self.__default_state = self.__states[0]
self.__current_state = self.__default_state
-
+
def addStates(self, states):
self.lock.acquire()
try:
@@ -30,19 +30,19 @@ class StateMachine(object):
raise IndexError("The state '%s' is already in the StateMachine." % state)
self.__states.append(state)
finally: self.lock.release()
-
-
+
+
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
- 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
+ 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
is not in `from_state`.
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
- indefinitely' flag since this leads to deadlock. If you want to wait indefinitely,
+ `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,
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
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
- 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 )`.
'''
- 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)
-
-
+
+
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`.
'''
- 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")
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)
- 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)
start = time.time()
while not self.lock.acquire(False):
time.sleep(.001)
if (start + wait - time.time()) <= 0.0:
- logging.debug("Could not acquire lock")
+ log.debug("Could not acquire lock")
return False
while not self.__current_state in from_states:
# detect timeout:
remainder = start + wait - time.time()
- if remainder > 0:
+ if remainder > 0:
self.notifier.wait(remainder)
- else:
- logging.debug("State was not ready")
+ else:
+ log.debug("State was not ready")
self.lock.release()
return False
@@ -105,9 +105,9 @@ class StateMachine(object):
# 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
- # some 'false' value returned from func,
+ # some 'false' value returned from func,
# 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)
self._set_state(to_state)
@@ -115,7 +115,7 @@ class StateMachine(object):
else:
log.error("StateMachine bug!! The lock should ensure this doesn't happen!")
return False
- finally:
+ finally:
self.notifier.set() # notify any waiting threads that the state has changed.
self.notifier.clear()
self.lock.release()
@@ -125,13 +125,13 @@ class StateMachine(object):
'''
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:
-
+
::
with state_machine.transition_ctx('one','two', wait=5) as locked:
if locked:
- # the state machine is currently locked in state 'one', and will
- # transition to 'two' when the 'with' statement ends, so long as
+ # the state machine is currently locked in state 'one', and will
+ # transition to 'two' when the 'with' statement ends, so long as
# no exception is thrown.
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']
- The other main difference between this method and `transition()` is that the
- state machine is locked for the duration of the `with` statement. Normally,
- after a `transition()` occurs, the state machine is immediately unlocked and
+ The other main difference between this method and `transition()` is that the
+ state machine is locked for the duration of the `with` statement. Normally,
+ after a `transition()` occurs, the state machine is immediately unlocked and
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)
- 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)
return _StateCtx(self, from_state, to_state, wait)
-
+
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`.
@@ -168,24 +168,24 @@ class StateMachine(object):
Ensure we are currently in one of the given `states` or wait until
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
- 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
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')
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)
- # 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
+ # 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
# 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.
# will return immediately if no transition is in process.
@@ -196,16 +196,16 @@ class StateMachine(object):
else: self.notifier.wait()
start = time.time()
- while not self.__current_state in states:
+ while not self.__current_state in states:
# detect timeout:
remainder = start + wait - time.time()
if remainder > 0: self.notifier.wait(remainder)
else: return False
return True
-
+
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)
@@ -231,7 +231,7 @@ class StateMachine(object):
def __str__(self):
return "".join(("StateMachine(", ','.join(self.__states), "): ", self.__current_state))
-
+
class _StateCtx:
@@ -244,28 +244,28 @@ class _StateCtx:
def __enter__(self):
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:
remainder = start + self.wait - time.time()
if remainder > 0: self.state_machine.notifier.wait(remainder)
- else:
+ else:
log.debug('StateMachine timeout while waiting for state: %s', self.from_state)
return False
self._locked = True # lock has been acquired at this point
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())
return True
def __exit__(self, exc_type, exc_val, exc_tb):
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)
if self._locked:
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._set_state(self.to_state)
diff --git a/sleekxmpp/xmlstream/handler/waiter.py b/sleekxmpp/xmlstream/handler/waiter.py
index 80720226..a4bc3545 100644
--- a/sleekxmpp/xmlstream/handler/waiter.py
+++ b/sleekxmpp/xmlstream/handler/waiter.py
@@ -16,6 +16,9 @@ from sleekxmpp.xmlstream import StanzaBase, RESPONSE_TIMEOUT
from sleekxmpp.xmlstream.handler.base import BaseHandler
+log = logging.getLogger(__name__)
+
+
class Waiter(BaseHandler):
"""
@@ -85,7 +88,7 @@ class Waiter(BaseHandler):
stanza = self._payload.get(True, timeout)
except queue.Empty:
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)
return stanza
diff --git a/sleekxmpp/xmlstream/matcher/xmlmask.py b/sleekxmpp/xmlstream/matcher/xmlmask.py
index 2967a2af..6ebb437d 100644
--- a/sleekxmpp/xmlstream/matcher/xmlmask.py
+++ b/sleekxmpp/xmlstream/matcher/xmlmask.py
@@ -6,6 +6,8 @@
See the file LICENSE for copying permission.
"""
+import logging
+
from xml.parsers.expat import ExpatError
from sleekxmpp.xmlstream.stanzabase import ET
@@ -18,6 +20,9 @@ from sleekxmpp.xmlstream.matcher.base import MatcherBase
IGNORE_NS = False
+log = logging.getLogger(__name__)
+
+
class MatchXMLMask(MatcherBase):
"""
@@ -97,8 +102,7 @@ class MatchXMLMask(MatcherBase):
try:
mask = ET.fromstring(mask)
except ExpatError:
- logging.log(logging.WARNING,
- "Expat error: %s\nIn parsing: %s" % ('', mask))
+ log.warning("Expat error: %s\nIn parsing: %s" % ('', mask))
if not use_ns:
# Compare the element without using namespaces.
diff --git a/sleekxmpp/xmlstream/scheduler.py b/sleekxmpp/xmlstream/scheduler.py
index 240d4a4b..14359102 100644
--- a/sleekxmpp/xmlstream/scheduler.py
+++ b/sleekxmpp/xmlstream/scheduler.py
@@ -15,6 +15,9 @@ except ImportError:
import Queue as queue
+log = logging.getLogger(__name__)
+
+
class Task(object):
"""
@@ -146,6 +149,8 @@ class Scheduler(object):
if wait <= 0.0:
newtask = self.addq.get(False)
else:
+ if wait >= 3.0:
+ wait = 3.0
newtask = self.addq.get(True, wait)
except queue.Empty:
cleanup = []
@@ -168,13 +173,13 @@ class Scheduler(object):
except KeyboardInterrupt:
self.run = False
if self.parentstop is not None:
- logging.debug("stopping parent")
+ log.debug("stopping parent")
self.parentstop.set()
except SystemExit:
self.run = False
if self.parentstop is not None:
self.parentstop.set()
- logging.debug("Quitting Scheduler thread")
+ log.debug("Quitting Scheduler thread")
if self.parentqueue is not None:
self.parentqueue.put(('quit', None, None))
diff --git a/sleekxmpp/xmlstream/stanzabase.py b/sleekxmpp/xmlstream/stanzabase.py
index f4d66aa8..aabd3864 100644
--- a/sleekxmpp/xmlstream/stanzabase.py
+++ b/sleekxmpp/xmlstream/stanzabase.py
@@ -16,6 +16,9 @@ from sleekxmpp.xmlstream import JID
from sleekxmpp.xmlstream.tostring import tostring
+log = logging.getLogger(__name__)
+
+
# Used to check if an argument is an XML object.
XML_TYPE = type(ET.Element('xml'))
@@ -1140,7 +1143,7 @@ class StanzaBase(ElementBase):
Meant to be overridden.
"""
- logging.exception('Error handling {%s}%s stanza' % (self.namespace,
+ log.exception('Error handling {%s}%s stanza' % (self.namespace,
self.name))
def send(self):
diff --git a/sleekxmpp/xmlstream/xmlstream.py b/sleekxmpp/xmlstream/xmlstream.py
index ace93cc3..30b76ce7 100644
--- a/sleekxmpp/xmlstream/xmlstream.py
+++ b/sleekxmpp/xmlstream/xmlstream.py
@@ -44,6 +44,9 @@ HANDLER_THREADS = 1
SSL_SUPPORT = True
+log = logging.getLogger(__name__)
+
+
class RestartStream(Exception):
"""
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.
socket -- The connection to the server.
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
connection state.
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.ssl_support = SSL_SUPPORT
+ self.ssl_version = ssl.PROTOCOL_TLSv1
self.state = StateMachine(('disconnected', 'connected'))
self.state._set_state('disconnected')
@@ -196,8 +202,15 @@ class XMLStream(object):
self.auto_reconnect = True
self.is_client = False
- signal.signal(signal.SIGHUP, self._handle_kill)
- signal.signal(signal.SIGTERM, self._handle_kill) # used in Windows
+ try:
+ 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):
"""
@@ -265,7 +278,7 @@ class XMLStream(object):
self.socket = self.socket_class(Socket.AF_INET, Socket.SOCK_STREAM)
self.socket.settimeout(None)
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)
if hasattr(self.socket, 'socket'):
# We are using a testing socket, so preserve the top
@@ -275,7 +288,7 @@ class XMLStream(object):
self.socket = ssl_socket
try:
- logging.debug("Connecting to %s:%s" % self.address)
+ log.debug("Connecting to %s:%s" % self.address)
self.socket.connect(self.address)
self.set_socket(self.socket, ignore=True)
#this event is where you should set your application state
@@ -283,7 +296,7 @@ class XMLStream(object):
return True
except Socket.error as serr:
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))
time.sleep(1)
return False
@@ -328,10 +341,10 @@ class XMLStream(object):
"""
Reset the stream's state and reconnect to the server.
"""
- logging.debug("reconnecting...")
+ log.debug("reconnecting...")
self.state.transition('connected', 'disconnected', wait=2.0,
func=self._disconnect, args=(True,))
- logging.debug("connecting...")
+ log.debug("connecting...")
return self.state.transition('disconnected', 'connected',
wait=2.0, func=self._connect)
@@ -368,9 +381,10 @@ class XMLStream(object):
to be restarted.
"""
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_version=ssl.PROTOCOL_TLSv1,
+ ssl_version=self.ssl_version,
do_handshake_on_connect=False)
if hasattr(self.socket, 'socket'):
# We are using a testing socket, so preserve the top
@@ -382,7 +396,7 @@ class XMLStream(object):
self.set_socket(self.socket)
return True
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
def start_stream_handler(self, xml):
@@ -517,6 +531,17 @@ class XMLStream(object):
self.__event_handlers[name] = filter(filter_pointers,
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):
"""
Manually trigger a custom event.
@@ -525,13 +550,22 @@ class XMLStream(object):
name -- The name of the event to trigger.
data -- Data that will be passed to each event handler.
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, []):
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:
self.event_queue.put(('event', handler, copy.copy(data)))
+
if handler[2]:
# If the handler is disposable, we will go ahead and
# remove it now instead of waiting for it to be
@@ -591,7 +625,7 @@ class XMLStream(object):
mask = mask.xml
data = str(data)
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(),
MatchXMLMask(mask))
self.register_handler(wait_for)
@@ -648,7 +682,7 @@ class XMLStream(object):
self.__thread[name].start()
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('send_thread', self._send_thread)
@@ -686,16 +720,16 @@ class XMLStream(object):
if self.is_client:
self.send_raw(self.stream_header)
except KeyboardInterrupt:
- logging.debug("Keyboard Escape Detected in _process")
+ log.debug("Keyboard Escape Detected in _process")
self.stop.set()
except SystemExit:
- logging.debug("SystemExit in _process")
+ log.debug("SystemExit in _process")
self.stop.set()
except Socket.error:
- logging.exception('Socket Error')
+ log.exception('Socket Error')
except:
if not self.stop.isSet():
- logging.exception('Connection error.')
+ log.exception('Connection error.')
if not self.stop.isSet() and self.auto_reconnect:
self.reconnect()
else:
@@ -725,7 +759,7 @@ class XMLStream(object):
if depth == 0:
# The stream's root element has closed,
# terminating the stream.
- logging.debug("End of stream recieved")
+ log.debug("End of stream recieved")
self.stream_end_event.set()
return False
elif depth == 1:
@@ -739,7 +773,29 @@ class XMLStream(object):
# Keep the root element empty of children to
# save on memory use.
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):
"""
@@ -750,7 +806,7 @@ class XMLStream(object):
Arguments:
xml -- The XML stanza to analyze.
"""
- logging.debug("RECV: %s" % tostring(xml,
+ log.debug("RECV: %s" % tostring(xml,
xmlns=self.default_ns,
stream=self))
# Apply any preprocessing filters.
@@ -788,7 +844,7 @@ class XMLStream(object):
def _threaded_event_wrapper(self, func, args):
"""
- Capture exceptions for event handlers that run
+ Capture exceptions for event handlers that run
in individual threads.
Arguments:
@@ -799,7 +855,7 @@ class XMLStream(object):
func(*args)
except Exception as e:
error_msg = 'Error processing event handler: %s'
- logging.exception(error_msg % str(func))
+ log.exception(error_msg % str(func))
if hasattr(args[0], 'exception'):
args[0].exception(e)
@@ -812,7 +868,7 @@ class XMLStream(object):
Stream event handlers will all execute in this thread. Custom event
handlers may be spawned in individual threads.
"""
- logging.debug("Loading event runner")
+ log.debug("Loading event runner")
try:
while not self.stop.isSet():
try:
@@ -830,14 +886,14 @@ class XMLStream(object):
handler.run(args[0])
except Exception as e:
error_msg = 'Error processing stream handler: %s'
- logging.exception(error_msg % handler.name)
+ log.exception(error_msg % handler.name)
args[0].exception(e)
elif etype == 'schedule':
try:
- logging.debug(args)
+ log.debug(args)
handler(*args[0])
except:
- logging.exception('Error processing scheduled task')
+ log.exception('Error processing scheduled task')
elif etype == 'event':
func, threaded, disposable = handler
try:
@@ -851,14 +907,14 @@ class XMLStream(object):
func(*args)
except Exception as e:
error_msg = 'Error processing event handler: %s'
- logging.exception(error_msg % str(func))
+ log.exception(error_msg % str(func))
if hasattr(args[0], 'exception'):
args[0].exception(e)
elif etype == 'quit':
- logging.debug("Quitting event runner thread")
+ log.debug("Quitting event runner thread")
return False
except KeyboardInterrupt:
- logging.debug("Keyboard Escape Detected in _event_runner")
+ log.debug("Keyboard Escape Detected in _event_runner")
self.disconnect()
return
except SystemExit:
@@ -876,14 +932,14 @@ class XMLStream(object):
data = self.send_queue.get(True, 1)
except queue.Empty:
continue
- logging.debug("SEND: %s" % data)
+ log.debug("SEND: %s" % data)
try:
self.socket.send(data.encode('utf-8'))
except:
- logging.warning("Failed to send %s" % data)
+ log.warning("Failed to send %s" % data)
self.disconnect(self.auto_reconnect)
except KeyboardInterrupt:
- logging.debug("Keyboard Escape Detected in _send_thread")
+ log.debug("Keyboard Escape Detected in _send_thread")
self.disconnect()
return
except SystemExit: