diff options
Diffstat (limited to 'sleekxmpp/plugins')
-rw-r--r-- | sleekxmpp/plugins/__init__.py | 4 | ||||
-rw-r--r-- | sleekxmpp/plugins/gmail_notify.py | 25 | ||||
-rw-r--r-- | sleekxmpp/plugins/jobs.py | 10 | ||||
-rw-r--r-- | sleekxmpp/plugins/old_0004.py | 80 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0004.py | 27 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0012.py | 7 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0030.py | 608 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0045.py | 89 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0060.py | 40 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0078.py | 17 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0085.py | 23 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0199.py | 98 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0202.py | 7 |
13 files changed, 541 insertions, 494 deletions
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):
|