Logging no longer uses root logger.

Each module should now log into its own logger.
This commit is contained in:
Lance Stout 2010-11-06 01:28:59 -04:00
parent d0c506f930
commit 4fb77ac878
22 changed files with 342 additions and 272 deletions

View file

@ -26,6 +26,9 @@ from sleekxmpp.xmlstream.matcher import *
from sleekxmpp.xmlstream.handler import *
log = logging.getLogger(__name__)
# Flag indicating if DNS SRV records are available for use.
SRV_SUPPORT = True
try:
@ -192,9 +195,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):
"""
@ -228,7 +231,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):
@ -446,12 +449,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
@ -459,12 +462,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
@ -472,12 +475,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
@ -485,12 +488,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
@ -498,17 +501,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):
@ -588,7 +591,7 @@ class BaseXMPP(XMLStream):
# disconnects. Determine if this was the last connection
# for the JID.
if show == 'unavailable':
logging.debug("%s %s got offline" % (jid, resource))
log.debug("%s %s got offline" % (jid, resource))
del connections[resource]
if not connections and not self.roster[jid]['in_roster']:
@ -604,7 +607,7 @@ class BaseXMPP(XMLStream):
self.event("changed_status", presence)
if got_online:
self.event("got_online", presence)
logging.debug("STATUS: %s%s/%s[%s]: %s" % (name, jid, resource,
log.debug("STATUS: %s%s/%s[%s]: %s" % (name, jid, resource,
show, status))
def _handle_subscribe(self, presence):

View file

@ -32,6 +32,9 @@ except:
SRV_SUPPORT = False
log = logging.getLogger(__name__)
class ClientXMPP(BaseXMPP):
"""
@ -133,7 +136,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()):
@ -150,19 +153,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." + \
log.debug("No appropriate SRV record found." + \
" Using JID server name.")
else:
# Pick a random server, weighted by priority.
@ -276,7 +279,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
@ -286,7 +289,7 @@ class ClientXMPP(BaseXMPP):
Restarts the stream.
"""
logging.debug("Starting TLS")
log.debug("Starting TLS")
if self.start_tls():
raise RestartStream()
@ -300,7 +303,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,
@ -334,7 +337,7 @@ class ClientXMPP(BaseXMPP):
sasl_ns,
'ANONYMOUS'))
else:
logging.error("No appropriate login method.")
log.error("No appropriate login method.")
self.disconnect()
return True
@ -356,7 +359,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()
@ -367,7 +370,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:
@ -381,10 +384,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")
@ -399,7 +402,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")

View file

@ -22,6 +22,9 @@ 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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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')

View file

@ -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):

View file

@ -13,6 +13,10 @@ 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'
@ -222,18 +226,18 @@ class xep_0030(base.base_plugin):
def handle_item_query(self, iq):
if iq['type'] == 'get':
logging.debug("Items requested by %s" % iq['from'])
log.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'])
log.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'])
log.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'])
log.debug("Info result from %s" % iq['from'])
self.xmpp.event('disco_info', iq)
def handle_disco_info(self, iq, forwarded=False):
@ -248,13 +252,13 @@ class xep_0030(base.base_plugin):
if not node_name:
node_name = 'main'
logging.debug("Using default handler for disco#info on node '%s'." % node_name)
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:
logging.debug("Node %s requested, but does not exist." % node_name)
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'
@ -276,13 +280,13 @@ class xep_0030(base.base_plugin):
if not node_name:
node_name = 'main'
logging.debug("Using default handler for disco#items on node '%s'." % node_name)
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:
logging.debug("Node %s requested, but does not exist." % node_name)
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'

View file

@ -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,7 @@ 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))
def handle_groupchat_presence(self, pr):
""" Handle a presence in a muc.
"""
@ -135,27 +139,27 @@ 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 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 +180,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 +198,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 +224,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 +250,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 +260,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 +283,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 +295,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 +311,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 +328,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.
"""

View file

@ -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

View file

@ -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")

View file

@ -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)

View file

@ -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,6 +10,10 @@ from . import base
import time
import logging
log = logging.getLogger(__name__)
class xep_0199(base.base_plugin):
"""XEP-0199 XMPP Ping"""
@ -20,19 +24,19 @@ class xep_0199(base.base_plugin):
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('urn:xmpp:ping')
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.")
log.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.boundjid.domain)

View file

@ -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):

View file

@ -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()

View file

@ -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)

View file

@ -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

View file

@ -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.

View file

@ -15,6 +15,9 @@ except ImportError:
import Queue as queue
log = logging.getLogger(__name__)
class Task(object):
"""
@ -168,13 +171,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))

View file

@ -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):

View file

@ -44,6 +44,9 @@ HANDLER_THREADS = 1
SSL_SUPPORT = True
log = logging.getLogger(__name__)
class RestartStream(Exception):
"""
Exception to restart stream processing, including
@ -206,7 +209,7 @@ class XMLStream(object):
# Used in Windows
signal.signal(signal.SIGTERM, self._handle_kill)
except:
logging.debug("Can not set interrupt signal handlers. " + \
log.debug("Can not set interrupt signal handlers. " + \
"SleekXMPP is not running from a main thread.")
def _handle_kill(self, signum, frame):
@ -275,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
@ -285,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
@ -293,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
@ -338,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)
@ -378,8 +381,8 @@ class XMLStream(object):
to be restarted.
"""
if self.ssl_support:
logging.info("Negotiating TLS")
logging.info("Using SSL version: %s" % str(self.ssl_version))
log.info("Negotiating TLS")
log.info("Using SSL version: %s" % str(self.ssl_version))
ssl_socket = ssl.wrap_socket(self.socket,
ssl_version=self.ssl_version,
do_handshake_on_connect=False)
@ -393,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):
@ -547,7 +550,7 @@ 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, skipping the
direct -- Runs the event directly if True, skipping the
event queue. All event handlers will run in the
same thread.
"""
@ -557,7 +560,7 @@ class XMLStream(object):
handler[0](copy.copy(data))
except Exception as e:
error_msg = 'Error processing event handler: %s'
logging.exception(error_msg % str(handler[0]))
log.exception(error_msg % str(handler[0]))
if hasattr(data, 'exception'):
data.exception(e)
else:
@ -622,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)
@ -679,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)
@ -717,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:
@ -756,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:
@ -770,7 +773,7 @@ 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):
"""
@ -781,7 +784,7 @@ class XMLStream(object):
Arguments:
xml -- The XML object to convert into a stanza object.
default_ns -- Optional default namespace to use instead of the
default_ns -- Optional default namespace to use instead of the
stream's current default namespace.
"""
if default_ns is None:
@ -803,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.
@ -852,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)
@ -865,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:
@ -883,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:
@ -904,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:
@ -929,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: