fixed todo merge
This commit is contained in:
@@ -1,20 +1,8 @@
|
||||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2007 Nathanael C. Fritz
|
||||
Copyright (C) 2010 Nathanael C. Fritz
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
SleekXMPP is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
SleekXMPP is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with SleekXMPP; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
__all__ = ['xep_0004', 'xep_0030', 'xep_0045', 'xep_0050', 'xep_0078', 'xep_0092', 'xep_0199', 'gmail_notify', 'xep_0060']
|
||||
__all__ = ['xep_0004', 'xep_0030', 'xep_0033', 'xep_0045', 'xep_0050', 'xep_0078', 'xep_0085', 'xep_0092', 'xep_0199', 'gmail_notify', 'xep_0060']
|
||||
|
||||
@@ -1,22 +1,12 @@
|
||||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2007 Nathanael C. Fritz
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
SleekXMPP is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
SleekXMPP is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with SleekXMPP; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2010 Nathanael C. Fritz
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
|
||||
|
||||
class base_plugin(object):
|
||||
|
||||
def __init__(self, xmpp, config):
|
||||
|
||||
@@ -1,57 +1,146 @@
|
||||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2007 Nathanael C. Fritz
|
||||
This file is part of SleekXMPP.
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
SleekXMPP is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
SleekXMPP is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with SleekXMPP; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
from __future__ import with_statement
|
||||
from . import base
|
||||
|
||||
import logging
|
||||
from xml.etree import cElementTree as ET
|
||||
import traceback
|
||||
import time
|
||||
from . import base
|
||||
from .. xmlstream.handler.callback import Callback
|
||||
from .. xmlstream.matcher.xpath import MatchXPath
|
||||
from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID
|
||||
from .. stanza.iq import Iq
|
||||
|
||||
|
||||
class GmailQuery(ElementBase):
|
||||
namespace = 'google:mail:notify'
|
||||
name = 'query'
|
||||
plugin_attrib = 'gmail'
|
||||
interfaces = set(('newer-than-time', 'newer-than-tid', 'q', 'search'))
|
||||
|
||||
def getSearch(self):
|
||||
return self['q']
|
||||
|
||||
def setSearch(self, search):
|
||||
self['q'] = search
|
||||
|
||||
def delSearch(self):
|
||||
del self['q']
|
||||
|
||||
|
||||
class MailBox(ElementBase):
|
||||
namespace = 'google:mail:notify'
|
||||
name = 'mailbox'
|
||||
plugin_attrib = 'mailbox'
|
||||
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,
|
||||
MailThread.name)):
|
||||
threads.append(MailThread(xml=threadXML, parent=None))
|
||||
return threads
|
||||
|
||||
def getMatched(self):
|
||||
return self['total-matched']
|
||||
|
||||
def getEstimate(self):
|
||||
return self['total-estimate'] == '1'
|
||||
|
||||
|
||||
class MailThread(ElementBase):
|
||||
namespace = 'google:mail:notify'
|
||||
name = 'mail-thread-info'
|
||||
plugin_attrib = 'thread'
|
||||
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)
|
||||
if sendersXML is not None:
|
||||
for senderXML in sendersXML.findall('{%s}sender' % self.namespace):
|
||||
senders.append(MailSender(xml=senderXML, parent=None))
|
||||
return senders
|
||||
|
||||
|
||||
class MailSender(ElementBase):
|
||||
namespace = 'google:mail:notify'
|
||||
name = 'sender'
|
||||
plugin_attrib = 'sender'
|
||||
interfaces = set(('address', 'name', 'originator', 'unread'))
|
||||
|
||||
def getOriginator(self):
|
||||
return self.xml.attrib.get('originator', '0') == '1'
|
||||
|
||||
def getUnread(self):
|
||||
return self.xml.attrib.get('unread', '0') == '1'
|
||||
|
||||
|
||||
class NewMail(ElementBase):
|
||||
namespace = 'google:mail:notify'
|
||||
name = 'new-mail'
|
||||
plugin_attrib = 'new-mail'
|
||||
|
||||
|
||||
class gmail_notify(base.base_plugin):
|
||||
|
||||
def plugin_init(self):
|
||||
self.description = 'Google Talk Gmail Notification'
|
||||
self.xmpp.add_event_handler('sent_presence', self.handler_gmailcheck, threaded=True)
|
||||
self.emails = []
|
||||
|
||||
def handler_gmailcheck(self, payload):
|
||||
#TODO XEP 30 should cache results and have getFeature
|
||||
result = self.xmpp['xep_0030'].getInfo(self.xmpp.server)
|
||||
features = []
|
||||
for feature in result.findall('{http://jabber.org/protocol/disco#info}query/{http://jabber.org/protocol/disco#info}feature'):
|
||||
features.append(feature.get('var'))
|
||||
if 'google:mail:notify' in features:
|
||||
logging.debug("Server supports Gmail Notify")
|
||||
self.xmpp.add_handler("<iq type='set' xmlns='%s'><new-mail xmlns='google:mail:notify' /></iq>" % self.xmpp.default_ns, self.handler_notify)
|
||||
self.getEmail()
|
||||
|
||||
def handler_notify(self, xml):
|
||||
logging.info("New Gmail recieved!")
|
||||
self.xmpp.event('gmail_notify')
|
||||
|
||||
def getEmail(self):
|
||||
iq = self.xmpp.makeIqGet()
|
||||
iq.attrib['from'] = self.xmpp.fulljid
|
||||
iq.attrib['to'] = self.xmpp.jid
|
||||
self.xmpp.makeIqQuery(iq, 'google:mail:notify')
|
||||
emails = iq.send()
|
||||
mailbox = emails.find('{google:mail:notify}mailbox')
|
||||
total = int(mailbox.get('total-matched', 0))
|
||||
logging.info("%s New Gmail Messages" % total)
|
||||
"""
|
||||
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,
|
||||
MailBox.namespace,
|
||||
MailBox.name)),
|
||||
self.handle_gmail))
|
||||
|
||||
self.xmpp.registerHandler(
|
||||
Callback('Gmail New Mail',
|
||||
MatchXPath('{%s}iq/{%s}%s' % (self.xmpp.default_ns,
|
||||
NewMail.namespace,
|
||||
NewMail.name)),
|
||||
self.handle_new_mail))
|
||||
|
||||
registerStanzaPlugin(Iq, GmailQuery)
|
||||
registerStanzaPlugin(Iq, MailBox)
|
||||
registerStanzaPlugin(Iq, NewMail)
|
||||
|
||||
self.last_result_time = None
|
||||
|
||||
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']))
|
||||
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!")
|
||||
self.xmpp.event('gmail_notify')
|
||||
self.checkEmail()
|
||||
|
||||
def getEmail(self, query=None):
|
||||
return self.search(query)
|
||||
|
||||
def checkEmail(self):
|
||||
return self.search(newer=self.last_result_time)
|
||||
|
||||
def search(self, query=None, newer=None):
|
||||
if query is None:
|
||||
logging.info("Gmail: Checking for new emails")
|
||||
else:
|
||||
logging.info('Gmail: Searching for emails matching: "%s"' % query)
|
||||
iq = self.xmpp.Iq()
|
||||
iq['type'] = 'get'
|
||||
iq['to'] = self.xmpp.jid
|
||||
iq['gmail']['q'] = query
|
||||
iq['gmail']['newer-than-time'] = newer
|
||||
return iq.send()
|
||||
|
||||
417
sleekxmpp/plugins/old_0004.py
Normal file
417
sleekxmpp/plugins/old_0004.py
Normal file
@@ -0,0 +1,417 @@
|
||||
"""
|
||||
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
|
||||
from xml.etree import cElementTree as ET
|
||||
import copy
|
||||
import logging
|
||||
#TODO support item groups and results
|
||||
|
||||
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.")
|
||||
|
||||
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)
|
||||
return form
|
||||
|
||||
def makeForm(self, ftype='form', title='', instructions=''):
|
||||
return Form(self.xmpp, ftype, title, instructions)
|
||||
|
||||
class FieldContainer(object):
|
||||
def __init__(self, stanza = 'form'):
|
||||
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__')])
|
||||
self.field[xml.get('var', '__unnamed__')].buildField(xml)
|
||||
|
||||
def buildContainer(self, xml):
|
||||
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=''):
|
||||
if not ftype in self.types:
|
||||
raise ValueError("Invalid Form Type")
|
||||
FieldContainer.__init__(self)
|
||||
self.xmpp = xmpp
|
||||
self.type = ftype
|
||||
self.title = title
|
||||
self.instructions = instructions
|
||||
self.reported = []
|
||||
self.items = []
|
||||
|
||||
def merge(self, form2):
|
||||
form1 = Form(ftype=self.type)
|
||||
form1.fromXML(self.getXML(self.type))
|
||||
for field in form2.fields:
|
||||
if not field.var in form1.field:
|
||||
form1.addField(field.var, field.type, field.label, field.desc, field.required, field.value)
|
||||
else:
|
||||
form1.field[field.var].value = field.value
|
||||
for option, label in field.options:
|
||||
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:
|
||||
value = field.value
|
||||
if len(value) == 1:
|
||||
value = value[0]
|
||||
result[field.var] = value
|
||||
return result
|
||||
|
||||
def setValues(self, values={}):
|
||||
for field in values:
|
||||
if field in self.field:
|
||||
if isinstance(values[field], list) or isinstance(values[field], tuple):
|
||||
for value in values[field]:
|
||||
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)
|
||||
return newitem
|
||||
|
||||
def buildItem(self, xml):
|
||||
newitem = self.addItem()
|
||||
newitem.buildContainer(xml)
|
||||
|
||||
def addReported(self):
|
||||
reported = FieldContainer('reported')
|
||||
self.reported.append(reported)
|
||||
return reported
|
||||
|
||||
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:
|
||||
self.setTitle(xml.find('{jabber:x:data}title').text)
|
||||
if xml.find('{jabber:x:data}instructions') is not None:
|
||||
self.setInstructions(xml.find('{jabber:x:data}instructions').text)
|
||||
for field in xml.findall('{jabber:x:data}field'):
|
||||
self.buildField(field)
|
||||
for reported in xml.findall('{jabber:x:data}reported'):
|
||||
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:
|
||||
self.type = ftype
|
||||
form = ET.Element('{jabber:x:data}x')
|
||||
form.attrib['type'] = self.type
|
||||
if self.title and self.type in ('form', 'result'):
|
||||
title = ET.Element('{jabber:x:data}title')
|
||||
title.text = self.title
|
||||
form.append(title)
|
||||
if self.instructions and self.type == 'form':
|
||||
instructions = ET.Element('{jabber:x:data}instructions')
|
||||
instructions.text = self.instructions
|
||||
form.append(instructions)
|
||||
for field in self.fields:
|
||||
form.append(field.getXML(self.type))
|
||||
for reported in self.reported:
|
||||
form.append(reported.getXML('{jabber:x:data}reported'))
|
||||
for item in self.items:
|
||||
form.append(item.getXML(self.type))
|
||||
#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:
|
||||
title = ET.Element('h2')
|
||||
title.text = self.title
|
||||
form.append(title)
|
||||
if self.instructions:
|
||||
instructions = ET.Element('p')
|
||||
instructions.text = self.instructions
|
||||
form.append(instructions)
|
||||
for field in self.fields:
|
||||
form.append(field.getXHTML())
|
||||
for field in self.reported:
|
||||
form.append(field.getXHTML())
|
||||
for field in self.items:
|
||||
form.append(field.getXHTML())
|
||||
return form
|
||||
|
||||
|
||||
def makeSubmit(self):
|
||||
self.setType('submit')
|
||||
|
||||
class FormField(object):
|
||||
types = ('boolean', 'fixed', 'hidden', 'jid-multi', 'jid-single', 'list-multi', 'list-single', 'text-multi', 'text-private', 'text-single')
|
||||
listtypes = ('jid-multi', 'jid-single', 'list-multi', 'list-single')
|
||||
lbtypes = ('fixed', 'text-multi')
|
||||
def __init__(self, var, ftype='text-single', label='', desc='', required=False, value=None):
|
||||
if not ftype in self.types:
|
||||
raise ValueError("Invalid Field Type")
|
||||
self.type = ftype
|
||||
self.var = var
|
||||
self.label = label
|
||||
self.desc = desc
|
||||
self.options = []
|
||||
self.required = False
|
||||
self.value = []
|
||||
if self.type in self.listtypes:
|
||||
self.islist = True
|
||||
else:
|
||||
self.islist = False
|
||||
if self.type in self.lbtypes:
|
||||
self.islinebreak = True
|
||||
else:
|
||||
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]
|
||||
|
||||
def setFalse(self):
|
||||
if self.type == 'boolean':
|
||||
self.value = [False]
|
||||
|
||||
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'):
|
||||
value = True
|
||||
else:
|
||||
value = False
|
||||
if self.islinebreak and value is not None:
|
||||
self.value += value.split('\n')
|
||||
else:
|
||||
if len(self.value) and (not self.islist or self.type == 'list-single'):
|
||||
self.value = [value]
|
||||
else:
|
||||
self.value.append(value)
|
||||
|
||||
def delValue(self, value):
|
||||
if type(self.value) == type([]):
|
||||
try:
|
||||
idx = self.value.index(value)
|
||||
if idx != -1:
|
||||
self.value.pop(idx)
|
||||
except ValueError:
|
||||
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', '')
|
||||
for option in xml.findall('{jabber:x:data}option'):
|
||||
self.addOption(option.find('{jabber:x:data}value').text, option.get('label', ''))
|
||||
for value in xml.findall('{jabber:x:data}value'):
|
||||
self.setValue(value.text)
|
||||
if xml.find('{jabber:x:data}required') is not None:
|
||||
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':
|
||||
field.attrib['type'] = self.type
|
||||
if self.type != 'fixed':
|
||||
if self.var:
|
||||
field.attrib['var'] = self.var
|
||||
if self.label:
|
||||
field.attrib['label'] = self.label
|
||||
if ftype == 'form':
|
||||
for option in self.options:
|
||||
optionxml = ET.Element('{jabber:x:data}option')
|
||||
optionxml.attrib['label'] = option[1]
|
||||
optionval = ET.Element('{jabber:x:data}value')
|
||||
optionval.text = option[0]
|
||||
optionxml.append(optionval)
|
||||
field.append(optionxml)
|
||||
if self.required:
|
||||
required = ET.Element('{jabber:x:data}required')
|
||||
field.append(required)
|
||||
if self.desc:
|
||||
desc = ET.Element('{jabber:x:data}desc')
|
||||
desc.text = self.desc
|
||||
field.append(desc)
|
||||
for value in self.value:
|
||||
valuexml = ET.Element('{jabber:x:data}value')
|
||||
if value is True or value is False:
|
||||
if value:
|
||||
valuexml.text = '1'
|
||||
else:
|
||||
valuexml.text = '0'
|
||||
else:
|
||||
valuexml.text = value
|
||||
field.append(valuexml)
|
||||
return field
|
||||
|
||||
def getXHTML(self):
|
||||
field = ET.Element('div', {'class': 'xmpp-xforms-%s' % self.type})
|
||||
if self.label:
|
||||
label = ET.Element('p')
|
||||
label.text = "%s: " % self.label
|
||||
else:
|
||||
label = ET.Element('p')
|
||||
label.text = "%s: " % self.var
|
||||
field.append(label)
|
||||
if self.type == 'boolean':
|
||||
formf = ET.Element('input', {'type': 'checkbox', 'name': self.var})
|
||||
if len(self.value) and self.value[0] in (True, 'true', '1'):
|
||||
formf.attrib['checked'] = 'checked'
|
||||
elif self.type == 'fixed':
|
||||
formf = ET.Element('p')
|
||||
try:
|
||||
formf.text = ', '.join(self.value)
|
||||
except:
|
||||
pass
|
||||
field.append(formf)
|
||||
formf = ET.Element('input', {'type': 'hidden', 'name': self.var})
|
||||
try:
|
||||
formf.text = ', '.join(self.value)
|
||||
except:
|
||||
pass
|
||||
elif self.type == 'hidden':
|
||||
formf = ET.Element('input', {'type': 'hidden', 'name': self.var})
|
||||
try:
|
||||
formf.text = ', '.join(self.value)
|
||||
except:
|
||||
pass
|
||||
elif self.type in ('jid-multi', 'list-multi'):
|
||||
formf = ET.Element('select', {'name': self.var})
|
||||
for option in self.options:
|
||||
optf = ET.Element('option', {'value': option[0], 'multiple': 'multiple'})
|
||||
optf.text = option[1]
|
||||
if option[1] in self.value:
|
||||
optf.attrib['selected'] = 'selected'
|
||||
formf.append(option)
|
||||
elif self.type in ('jid-single', 'text-single'):
|
||||
formf = ET.Element('input', {'type': 'text', 'name': self.var})
|
||||
try:
|
||||
formf.attrib['value'] = ', '.join(self.value)
|
||||
except:
|
||||
pass
|
||||
elif self.type == 'list-single':
|
||||
formf = ET.Element('select', {'name': self.var})
|
||||
for option in self.options:
|
||||
optf = ET.Element('option', {'value': option[0]})
|
||||
optf.text = option[1]
|
||||
if not optf.text:
|
||||
optf.text = option[0]
|
||||
if option[1] in self.value:
|
||||
optf.attrib['selected'] = 'selected'
|
||||
formf.append(optf)
|
||||
elif self.type == 'text-multi':
|
||||
formf = ET.Element('textarea', {'name': self.var})
|
||||
try:
|
||||
formf.text = ', '.join(self.value)
|
||||
except:
|
||||
pass
|
||||
if not formf.text:
|
||||
formf.text = ' '
|
||||
elif self.type == 'text-private':
|
||||
formf = ET.Element('input', {'type': 'password', 'name': self.var})
|
||||
try:
|
||||
formf.attrib['value'] = ', '.join(self.value)
|
||||
except:
|
||||
pass
|
||||
label.append(formf)
|
||||
return field
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from .. xmlstream.stanzabase import ElementBase, ET, JID
|
||||
from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID
|
||||
from .. stanza.iq import Iq
|
||||
from .. stanza.message import Message
|
||||
from .. basexmpp import basexmpp
|
||||
@@ -6,9 +6,6 @@ from .. xmlstream.xmlstream import XMLStream
|
||||
import logging
|
||||
from . import xep_0004
|
||||
|
||||
def stanzaPlugin(stanza, plugin):
|
||||
stanza.plugin_attrib_map[plugin.plugin_attrib] = plugin
|
||||
stanza.plugin_tag_map["{%s}%s" % (plugin.namespace, plugin.name)] = plugin
|
||||
|
||||
class PubsubState(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/psstate'
|
||||
@@ -30,7 +27,7 @@ class PubsubState(ElementBase):
|
||||
for child in self.xml.getchildren():
|
||||
self.xml.remove(child)
|
||||
|
||||
stanzaPlugin(Iq, PubsubState)
|
||||
registerStanzaPlugin(Iq, PubsubState)
|
||||
|
||||
class PubsubStateEvent(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/psstate#event'
|
||||
@@ -40,8 +37,8 @@ class PubsubStateEvent(ElementBase):
|
||||
plugin_attrib_map = {}
|
||||
plugin_tag_map = {}
|
||||
|
||||
stanzaPlugin(Message, PubsubStateEvent)
|
||||
stanzaPlugin(PubsubStateEvent, PubsubState)
|
||||
registerStanzaPlugin(Message, PubsubStateEvent)
|
||||
registerStanzaPlugin(PubsubStateEvent, PubsubState)
|
||||
|
||||
class Pubsub(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub'
|
||||
@@ -51,7 +48,7 @@ class Pubsub(ElementBase):
|
||||
plugin_attrib_map = {}
|
||||
plugin_tag_map = {}
|
||||
|
||||
stanzaPlugin(Iq, Pubsub)
|
||||
registerStanzaPlugin(Iq, Pubsub)
|
||||
|
||||
class PubsubOwner(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#owner'
|
||||
@@ -61,7 +58,7 @@ class PubsubOwner(ElementBase):
|
||||
plugin_attrib_map = {}
|
||||
plugin_tag_map = {}
|
||||
|
||||
stanzaPlugin(Iq, PubsubOwner)
|
||||
registerStanzaPlugin(Iq, PubsubOwner)
|
||||
|
||||
class Affiliation(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub'
|
||||
@@ -86,7 +83,7 @@ class Affiliations(ElementBase):
|
||||
self.xml.append(affiliation.xml)
|
||||
return self.iterables.append(affiliation)
|
||||
|
||||
stanzaPlugin(Pubsub, Affiliations)
|
||||
registerStanzaPlugin(Pubsub, Affiliations)
|
||||
|
||||
|
||||
class Subscription(ElementBase):
|
||||
@@ -103,7 +100,7 @@ class Subscription(ElementBase):
|
||||
def getjid(self):
|
||||
return jid(self._getattr('jid'))
|
||||
|
||||
stanzaPlugin(Pubsub, Subscription)
|
||||
registerStanzaPlugin(Pubsub, Subscription)
|
||||
|
||||
class Subscriptions(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub'
|
||||
@@ -114,7 +111,7 @@ class Subscriptions(ElementBase):
|
||||
plugin_tag_map = {}
|
||||
subitem = (Subscription,)
|
||||
|
||||
stanzaPlugin(Pubsub, Subscriptions)
|
||||
registerStanzaPlugin(Pubsub, Subscriptions)
|
||||
|
||||
class OptionalSetting(object):
|
||||
interfaces = set(('required',))
|
||||
@@ -147,7 +144,7 @@ class SubscribeOptions(ElementBase, OptionalSetting):
|
||||
plugin_tag_map = {}
|
||||
interfaces = set(('required',))
|
||||
|
||||
stanzaPlugin(Subscription, SubscribeOptions)
|
||||
registerStanzaPlugin(Subscription, SubscribeOptions)
|
||||
|
||||
class Item(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub'
|
||||
@@ -178,7 +175,7 @@ class Items(ElementBase):
|
||||
plugin_tag_map = {}
|
||||
subitem = (Item,)
|
||||
|
||||
stanzaPlugin(Pubsub, Items)
|
||||
registerStanzaPlugin(Pubsub, Items)
|
||||
|
||||
class Create(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub'
|
||||
@@ -188,7 +185,7 @@ class Create(ElementBase):
|
||||
plugin_attrib_map = {}
|
||||
plugin_tag_map = {}
|
||||
|
||||
stanzaPlugin(Pubsub, Create)
|
||||
registerStanzaPlugin(Pubsub, Create)
|
||||
|
||||
#class Default(ElementBase):
|
||||
# namespace = 'http://jabber.org/protocol/pubsub'
|
||||
@@ -203,7 +200,7 @@ stanzaPlugin(Pubsub, Create)
|
||||
# if not t: t == 'leaf'
|
||||
# return t
|
||||
#
|
||||
#stanzaPlugin(Pubsub, Default)
|
||||
#registerStanzaPlugin(Pubsub, Default)
|
||||
|
||||
class Publish(Items):
|
||||
namespace = 'http://jabber.org/protocol/pubsub'
|
||||
@@ -214,7 +211,7 @@ class Publish(Items):
|
||||
plugin_tag_map = {}
|
||||
subitem = (Item,)
|
||||
|
||||
stanzaPlugin(Pubsub, Publish)
|
||||
registerStanzaPlugin(Pubsub, Publish)
|
||||
|
||||
class Retract(Items):
|
||||
namespace = 'http://jabber.org/protocol/pubsub'
|
||||
@@ -224,7 +221,7 @@ class Retract(Items):
|
||||
plugin_attrib_map = {}
|
||||
plugin_tag_map = {}
|
||||
|
||||
stanzaPlugin(Pubsub, Retract)
|
||||
registerStanzaPlugin(Pubsub, Retract)
|
||||
|
||||
class Unsubscribe(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub'
|
||||
@@ -254,13 +251,13 @@ class Subscribe(ElementBase):
|
||||
def getJid(self):
|
||||
return JID(self._getAttr('jid'))
|
||||
|
||||
stanzaPlugin(Pubsub, Subscribe)
|
||||
registerStanzaPlugin(Pubsub, Subscribe)
|
||||
|
||||
class Configure(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub'
|
||||
name = 'configure'
|
||||
plugin_attrib = name
|
||||
interfaces = set(('node', 'type', 'config'))
|
||||
interfaces = set(('node', 'type'))
|
||||
plugin_attrib_map = {}
|
||||
plugin_tag_map = {}
|
||||
|
||||
@@ -269,22 +266,8 @@ class Configure(ElementBase):
|
||||
if not t: t == 'leaf'
|
||||
return t
|
||||
|
||||
def getConfig(self):
|
||||
config = self.xml.find('{jabber:x:data}x')
|
||||
form = xep_0004.Form()
|
||||
if config is not None:
|
||||
form.fromXML(config)
|
||||
return form
|
||||
|
||||
def setConfig(self, value):
|
||||
self.xml.append(value.getXML())
|
||||
return self
|
||||
|
||||
def delConfig(self):
|
||||
config = self.xml.find('{jabber:x:data}x')
|
||||
self.xml.remove(config)
|
||||
|
||||
stanzaPlugin(Pubsub, Configure)
|
||||
registerStanzaPlugin(Pubsub, Configure)
|
||||
registerStanzaPlugin(Configure, xep_0004.Form)
|
||||
|
||||
class DefaultConfig(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#owner'
|
||||
@@ -296,28 +279,14 @@ class DefaultConfig(ElementBase):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
ElementBase.__init__(self, *args, **kwargs)
|
||||
|
||||
def getConfig(self):
|
||||
config = self.xml.find('{jabber:x:data}x')
|
||||
form = xep_0004.Form()
|
||||
if config is not None:
|
||||
form.fromXML(config)
|
||||
return form
|
||||
|
||||
def setConfig(self, value):
|
||||
self.xml.append(value.getXML())
|
||||
return self
|
||||
|
||||
def delConfig(self):
|
||||
config = self.xml.find('{jabber:x:data}x')
|
||||
self.xml.remove(config)
|
||||
|
||||
def getType(self):
|
||||
t = self._getAttr('type')
|
||||
if not t: t = 'leaf'
|
||||
return t
|
||||
|
||||
stanzaPlugin(PubsubOwner, DefaultConfig)
|
||||
registerStanzaPlugin(PubsubOwner, DefaultConfig)
|
||||
registerStanzaPlugin(DefaultConfig, xep_0004.Form)
|
||||
|
||||
class Options(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub'
|
||||
@@ -351,8 +320,8 @@ class Options(ElementBase):
|
||||
def getJid(self):
|
||||
return JID(self._getAttr('jid'))
|
||||
|
||||
stanzaPlugin(Pubsub, Options)
|
||||
stanzaPlugin(Subscribe, Options)
|
||||
registerStanzaPlugin(Pubsub, Options)
|
||||
registerStanzaPlugin(Subscribe, Options)
|
||||
|
||||
class OwnerAffiliations(Affiliations):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#owner'
|
||||
@@ -366,7 +335,7 @@ class OwnerAffiliations(Affiliations):
|
||||
self.xml.append(affiliation.xml)
|
||||
return self.affiliations.append(affiliation)
|
||||
|
||||
stanzaPlugin(PubsubOwner, OwnerAffiliations)
|
||||
registerStanzaPlugin(PubsubOwner, OwnerAffiliations)
|
||||
|
||||
class OwnerAffiliation(Affiliation):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#owner'
|
||||
@@ -380,7 +349,7 @@ class OwnerConfigure(Configure):
|
||||
plugin_attrib_map = {}
|
||||
plugin_tag_map = {}
|
||||
|
||||
stanzaPlugin(PubsubOwner, OwnerConfigure)
|
||||
registerStanzaPlugin(PubsubOwner, OwnerConfigure)
|
||||
|
||||
class OwnerDefault(OwnerConfigure):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#owner'
|
||||
@@ -388,7 +357,7 @@ class OwnerDefault(OwnerConfigure):
|
||||
plugin_attrib_map = {}
|
||||
plugin_tag_map = {}
|
||||
|
||||
stanzaPlugin(PubsubOwner, OwnerDefault)
|
||||
registerStanzaPlugin(PubsubOwner, OwnerDefault)
|
||||
|
||||
class OwnerDelete(ElementBase, OptionalSetting):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#owner'
|
||||
@@ -398,7 +367,7 @@ class OwnerDelete(ElementBase, OptionalSetting):
|
||||
plugin_tag_map = {}
|
||||
interfaces = set(('node',))
|
||||
|
||||
stanzaPlugin(PubsubOwner, OwnerDelete)
|
||||
registerStanzaPlugin(PubsubOwner, OwnerDelete)
|
||||
|
||||
class OwnerPurge(ElementBase, OptionalSetting):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#owner'
|
||||
@@ -407,7 +376,7 @@ class OwnerPurge(ElementBase, OptionalSetting):
|
||||
plugin_attrib_map = {}
|
||||
plugin_tag_map = {}
|
||||
|
||||
stanzaPlugin(PubsubOwner, OwnerPurge)
|
||||
registerStanzaPlugin(PubsubOwner, OwnerPurge)
|
||||
|
||||
class OwnerRedirect(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#owner'
|
||||
@@ -423,7 +392,7 @@ class OwnerRedirect(ElementBase):
|
||||
def getJid(self):
|
||||
return JID(self._getAttr('jid'))
|
||||
|
||||
stanzaPlugin(OwnerDelete, OwnerRedirect)
|
||||
registerStanzaPlugin(OwnerDelete, OwnerRedirect)
|
||||
|
||||
class OwnerSubscriptions(Subscriptions):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#owner'
|
||||
@@ -437,7 +406,7 @@ class OwnerSubscriptions(Subscriptions):
|
||||
self.xml.append(subscription.xml)
|
||||
return self.subscriptions.append(subscription)
|
||||
|
||||
stanzaPlugin(PubsubOwner, OwnerSubscriptions)
|
||||
registerStanzaPlugin(PubsubOwner, OwnerSubscriptions)
|
||||
|
||||
class OwnerSubscription(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#owner'
|
||||
@@ -461,7 +430,7 @@ class Event(ElementBase):
|
||||
plugin_attrib_map = {}
|
||||
plugin_tag_map = {}
|
||||
|
||||
stanzaPlugin(Message, Event)
|
||||
registerStanzaPlugin(Message, Event)
|
||||
|
||||
class EventItem(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#event'
|
||||
@@ -501,7 +470,7 @@ class EventItems(ElementBase):
|
||||
plugin_tag_map = {}
|
||||
subitem = (EventItem, EventRetract)
|
||||
|
||||
stanzaPlugin(Event, EventItems)
|
||||
registerStanzaPlugin(Event, EventItems)
|
||||
|
||||
class EventCollection(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#event'
|
||||
@@ -511,7 +480,7 @@ class EventCollection(ElementBase):
|
||||
plugin_attrib_map = {}
|
||||
plugin_tag_map = {}
|
||||
|
||||
stanzaPlugin(Event, EventCollection)
|
||||
registerStanzaPlugin(Event, EventCollection)
|
||||
|
||||
class EventAssociate(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#event'
|
||||
@@ -521,7 +490,7 @@ class EventAssociate(ElementBase):
|
||||
plugin_attrib_map = {}
|
||||
plugin_tag_map = {}
|
||||
|
||||
stanzaPlugin(EventCollection, EventAssociate)
|
||||
registerStanzaPlugin(EventCollection, EventAssociate)
|
||||
|
||||
class EventDisassociate(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#event'
|
||||
@@ -531,7 +500,7 @@ class EventDisassociate(ElementBase):
|
||||
plugin_attrib_map = {}
|
||||
plugin_tag_map = {}
|
||||
|
||||
stanzaPlugin(EventCollection, EventDisassociate)
|
||||
registerStanzaPlugin(EventCollection, EventDisassociate)
|
||||
|
||||
class EventConfiguration(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#event'
|
||||
@@ -541,22 +510,8 @@ class EventConfiguration(ElementBase):
|
||||
plugin_attrib_map = {}
|
||||
plugin_tag_map = {}
|
||||
|
||||
def getConfig(self):
|
||||
config = self.xml.find('{jabber:x:data}x')
|
||||
form = xep_0004.Form()
|
||||
if config is not None:
|
||||
form.fromXML(config)
|
||||
return form
|
||||
|
||||
def setConfig(self, value):
|
||||
self.xml.append(value.getXML())
|
||||
return self
|
||||
|
||||
def delConfig(self):
|
||||
config = self.xml.find('{jabber:x:data}x')
|
||||
self.xml.remove(config)
|
||||
|
||||
stanzaPlugin(Event, EventConfiguration)
|
||||
registerStanzaPlugin(Event, EventConfiguration)
|
||||
registerStanzaPlugin(EventConfiguration, xep_0004.Form)
|
||||
|
||||
class EventPurge(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#event'
|
||||
@@ -566,7 +521,7 @@ class EventPurge(ElementBase):
|
||||
plugin_attrib_map = {}
|
||||
plugin_tag_map = {}
|
||||
|
||||
stanzaPlugin(Event, EventPurge)
|
||||
registerStanzaPlugin(Event, EventPurge)
|
||||
|
||||
class EventSubscription(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/pubsub#event'
|
||||
@@ -582,4 +537,4 @@ class EventSubscription(ElementBase):
|
||||
def getJid(self):
|
||||
return JID(self._getAttr('jid'))
|
||||
|
||||
stanzaPlugin(Event, EventSubscription)
|
||||
registerStanzaPlugin(Event, EventSubscription)
|
||||
|
||||
@@ -1,427 +1,347 @@
|
||||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2007 Nathanael C. Fritz
|
||||
Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
SleekXMPP is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
SleekXMPP is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with SleekXMPP; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
from . import base
|
||||
|
||||
import logging
|
||||
from xml.etree import cElementTree as ET
|
||||
import copy
|
||||
#TODO support item groups and results
|
||||
from . import base
|
||||
from .. xmlstream.handler.callback import Callback
|
||||
from .. xmlstream.matcher.xpath import MatchXPath
|
||||
from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID
|
||||
from .. stanza.message import Message
|
||||
|
||||
|
||||
class Form(ElementBase):
|
||||
namespace = 'jabber:x:data'
|
||||
name = 'x'
|
||||
plugin_attrib = 'form'
|
||||
interfaces = set(('fields', 'instructions', 'items', 'reported', 'title', 'type', 'values'))
|
||||
sub_interfaces = set(('title',))
|
||||
form_types = set(('cancel', 'form', 'result', 'submit'))
|
||||
|
||||
def setup(self, xml=None):
|
||||
if ElementBase.setup(self, xml): #if we had to generate xml
|
||||
self['type'] = 'form'
|
||||
|
||||
def addField(self, var='', ftype=None, label='', desc='', required=False, value=None, options=None, **kwargs):
|
||||
kwtype = kwargs.get('type', None)
|
||||
if kwtype is None:
|
||||
kwtype = ftype
|
||||
|
||||
field = FormField(parent=self)
|
||||
field['var'] = var
|
||||
field['type'] = kwtype
|
||||
field['label'] = label
|
||||
field['desc'] = desc
|
||||
field['required'] = required
|
||||
field['value'] = value
|
||||
if options is not None:
|
||||
field['options'] = options
|
||||
return field
|
||||
|
||||
def getXML(self):
|
||||
logging.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")
|
||||
n = Form(xml=xml)
|
||||
return n
|
||||
|
||||
def addItem(self, values):
|
||||
itemXML = ET.Element('{%s}item' % self.namespace)
|
||||
self.xml.append(itemXML)
|
||||
reported_vars = self['reported'].keys()
|
||||
for var in reported_vars:
|
||||
fieldXML = ET.Element('{%s}field' % FormField.namespace)
|
||||
itemXML.append(fieldXML)
|
||||
field = FormField(xml=fieldXML)
|
||||
field['var'] = var
|
||||
field['value'] = values.get(var, None)
|
||||
|
||||
def addReported(self, var, ftype=None, label='', desc='', **kwargs):
|
||||
kwtype = kwargs.get('type', None)
|
||||
if kwtype is None:
|
||||
kwtype = ftype
|
||||
reported = self.xml.find('{%s}reported' % self.namespace)
|
||||
if reported is None:
|
||||
reported = ET.Element('{%s}reported' % self.namespace)
|
||||
self.xml.append(reported)
|
||||
fieldXML = ET.Element('{%s}field' % FormField.namespace)
|
||||
reported.append(fieldXML)
|
||||
field = FormField(xml=fieldXML)
|
||||
field['var'] = var
|
||||
field['type'] = kwtype
|
||||
field['label'] = label
|
||||
field['desc'] = desc
|
||||
return field
|
||||
|
||||
def cancel(self):
|
||||
self['type'] = 'cancel'
|
||||
|
||||
def delFields(self):
|
||||
fieldsXML = self.xml.findall('{%s}field' % FormField.namespace)
|
||||
for fieldXML in fieldsXML:
|
||||
self.xml.remove(fieldXML)
|
||||
|
||||
def delInstructions(self):
|
||||
instsXML = self.xml.findall('{%s}instructions')
|
||||
for instXML in instsXML:
|
||||
self.xml.remove(instXML)
|
||||
|
||||
def delItems(self):
|
||||
itemsXML = self.xml.find('{%s}item' % self.namespace)
|
||||
for itemXML in itemsXML:
|
||||
self.xml.remove(itemXML)
|
||||
|
||||
def delReported(self):
|
||||
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)
|
||||
for fieldXML in fieldsXML:
|
||||
field = FormField(xml=fieldXML)
|
||||
if use_dict:
|
||||
fields[field['var']] = field
|
||||
else:
|
||||
fields.append((field['var'], field))
|
||||
return fields
|
||||
|
||||
def getInstructions(self):
|
||||
instructions = ''
|
||||
instsXML = self.xml.findall('{%s}instructions' % self.namespace)
|
||||
return "\n".join([instXML.text for instXML in instsXML])
|
||||
|
||||
def getItems(self):
|
||||
items = []
|
||||
itemsXML = self.xml.findall('{%s}item' % self.namespace)
|
||||
for itemXML in itemsXML:
|
||||
item = {}
|
||||
fieldsXML = itemXML.findall('{%s}field' % FormField.namespace)
|
||||
for fieldXML in fieldsXML:
|
||||
field = FormField(xml=fieldXML)
|
||||
item[field['var']] = field['value']
|
||||
items.append(item)
|
||||
return items
|
||||
|
||||
def getReported(self):
|
||||
fields = {}
|
||||
fieldsXML = self.xml.findall('{%s}reported/{%s}field' % (self.namespace,
|
||||
FormField.namespace))
|
||||
for fieldXML in fieldsXML:
|
||||
field = FormField(xml=fieldXML)
|
||||
fields[field['var']] = field
|
||||
return fields
|
||||
|
||||
def getValues(self):
|
||||
values = {}
|
||||
fields = self.getFields(use_dict=True)
|
||||
for var in fields:
|
||||
values[var] = fields[var]['value']
|
||||
return values
|
||||
|
||||
def reply(self):
|
||||
if self['type'] == 'form':
|
||||
self['type'] = 'submit'
|
||||
elif self['type'] == 'submit':
|
||||
self['type'] = 'result'
|
||||
|
||||
def setFields(self, fields, default=None):
|
||||
del self['fields']
|
||||
for field_data in fields:
|
||||
var = field_data[0]
|
||||
field = field_data[1]
|
||||
field['var'] = var
|
||||
|
||||
self.addField(**field)
|
||||
|
||||
def setInstructions(self, instructions):
|
||||
del self['instructions']
|
||||
if instructions in [None, '']:
|
||||
return
|
||||
instructions = instructions.split('\n')
|
||||
for instruction in instructions:
|
||||
inst = ET.Element('{%s}instructions' % self.namespace)
|
||||
inst.text = instruction
|
||||
self.xml.append(inst)
|
||||
|
||||
def setItems(self, items):
|
||||
for item in items:
|
||||
self.addItem(item)
|
||||
|
||||
def setReported(self, reported, default=None):
|
||||
for var in reported:
|
||||
field = reported[var]
|
||||
field['var'] = var
|
||||
self.addReported(var, **field)
|
||||
|
||||
def setValues(self, values):
|
||||
fields = self.getFields(use_dict=True)
|
||||
for field in values:
|
||||
fields[field]['value'] = values[field]
|
||||
|
||||
|
||||
class FormField(ElementBase):
|
||||
namespace = 'jabber:x:data'
|
||||
name = 'field'
|
||||
plugin_attrib = 'field'
|
||||
interfaces = set(('answer', 'desc', 'required', 'value', 'options', 'label', 'type', 'var'))
|
||||
sub_interfaces = set(('desc',))
|
||||
field_types = set(('boolean', 'fixed', 'hidden', 'jid-multi', 'jid-single', 'list-multi',
|
||||
'list-single', 'text-multi', 'text-private', 'text-single'))
|
||||
multi_value_types = set(('hidden', 'jid-multi', 'list-multi', 'text-multi'))
|
||||
multi_line_types = set(('hidden', 'text-multi'))
|
||||
option_types = set(('list-multi', 'list-single'))
|
||||
true_values = set((True, '1', 'true'))
|
||||
|
||||
def addOption(self, label='', value=''):
|
||||
if self['type'] in self.option_types:
|
||||
opt = FieldOption(parent=self)
|
||||
opt['label'] = label
|
||||
opt['value'] = value
|
||||
else:
|
||||
raise ValueError("Cannot add options to a %s field." % self['type'])
|
||||
|
||||
def delOptions(self):
|
||||
optsXML = self.xml.findall('{%s}option' % self.namespace)
|
||||
for optXML in optsXML:
|
||||
self.xml.remove(optXML)
|
||||
|
||||
def delRequired(self):
|
||||
reqXML = self.xml.find('{%s}required' % self.namespace)
|
||||
if reqXML is not None:
|
||||
self.xml.remove(reqXML)
|
||||
|
||||
def delValue(self):
|
||||
valsXML = self.xml.findall('{%s}value' % self.namespace)
|
||||
for valXML in valsXML:
|
||||
self.xml.remove(valXML)
|
||||
|
||||
def getAnswer(self):
|
||||
return self.getValue()
|
||||
|
||||
def getOptions(self):
|
||||
options = []
|
||||
optsXML = self.xml.findall('{%s}option' % self.namespace)
|
||||
for optXML in optsXML:
|
||||
opt = FieldOption(xml=optXML)
|
||||
options.append({'label': opt['label'], 'value':opt['value']})
|
||||
return options
|
||||
|
||||
def getRequired(self):
|
||||
reqXML = self.xml.find('{%s}required' % self.namespace)
|
||||
return reqXML is not None
|
||||
|
||||
def getValue(self):
|
||||
valsXML = self.xml.findall('{%s}value' % self.namespace)
|
||||
if len(valsXML) == 0:
|
||||
return None
|
||||
elif self['type'] == 'boolean':
|
||||
return valsXML[0].text in self.true_values
|
||||
elif self['type'] in self.multi_value_types:
|
||||
values = []
|
||||
for valXML in valsXML:
|
||||
if valXML.text is None:
|
||||
valXML.text = ''
|
||||
values.append(valXML.text)
|
||||
if self['type'] == 'text-multi':
|
||||
values = "\n".join(values)
|
||||
return values
|
||||
else:
|
||||
return valsXML[0].text
|
||||
|
||||
def setAnswer(self, answer):
|
||||
self.setValue(answer)
|
||||
|
||||
def setFalse(self):
|
||||
self.setValue(False)
|
||||
|
||||
def setOptions(self, options):
|
||||
for value in options:
|
||||
if isinstance(value, dict):
|
||||
self.addOption(**value)
|
||||
else:
|
||||
self.addOption(value=value)
|
||||
|
||||
def setRequired(self, required):
|
||||
exists = self.getRequired()
|
||||
if not exists and required:
|
||||
self.xml.append(ET.Element('{%s}required' % self.namespace))
|
||||
elif exists and not required:
|
||||
self.delRequired()
|
||||
|
||||
def setTrue(self):
|
||||
self.setValue(True)
|
||||
|
||||
def setValue(self, value):
|
||||
self.delValue()
|
||||
valXMLName = '{%s}value' % self.namespace
|
||||
|
||||
if self['type'] == 'boolean':
|
||||
if value in self.true_values:
|
||||
valXML = ET.Element(valXMLName)
|
||||
valXML.text = '1'
|
||||
self.xml.append(valXML)
|
||||
else:
|
||||
valXML = ET.Element(valXMLName)
|
||||
valXML.text = '0'
|
||||
self.xml.append(valXML)
|
||||
elif self['type'] in self.multi_value_types or self['type'] in ['', None]:
|
||||
if self['type'] in self.multi_line_types and isinstance(value, str):
|
||||
value = value.split('\n')
|
||||
if not isinstance(value, list):
|
||||
value = [value]
|
||||
for val in value:
|
||||
if self['type'] in ['', None] and val in self.true_values:
|
||||
val = '1'
|
||||
valXML = ET.Element(valXMLName)
|
||||
valXML.text = val
|
||||
self.xml.append(valXML)
|
||||
else:
|
||||
if isinstance(value, list):
|
||||
raise ValueError("Cannot add multiple values to a %s field." % self['type'])
|
||||
valXML = ET.Element(valXMLName)
|
||||
valXML.text = value
|
||||
self.xml.append(valXML)
|
||||
|
||||
|
||||
class FieldOption(ElementBase):
|
||||
namespace = 'jabber:x:data'
|
||||
name = 'option'
|
||||
plugin_attrib = 'option'
|
||||
interfaces = set(('label', 'value'))
|
||||
sub_interfaces = set(('value',))
|
||||
|
||||
|
||||
class xep_0004(base.base_plugin):
|
||||
|
||||
"""
|
||||
XEP-0004: Data Forms
|
||||
"""
|
||||
|
||||
def plugin_init(self):
|
||||
self.xep = '0004'
|
||||
self.description = 'Data Forms'
|
||||
self.xmpp.add_handler("<message><x xmlns='jabber:x:data' /></message>", self.handler_message_xform)
|
||||
|
||||
self.xmpp.registerHandler(
|
||||
Callback('Data Form',
|
||||
MatchXPath('{%s}message/{%s}x' % (self.xmpp.default_ns,
|
||||
Form.namespace)),
|
||||
self.handle_form))
|
||||
|
||||
registerStanzaPlugin(FormField, FieldOption)
|
||||
registerStanzaPlugin(Form, FormField)
|
||||
registerStanzaPlugin(Message, Form)
|
||||
|
||||
def post_init(self):
|
||||
base.base_plugin.post_init(self)
|
||||
self.xmpp.plugin['xep_0030'].add_feature('jabber:x:data')
|
||||
|
||||
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)
|
||||
return form
|
||||
|
||||
def makeForm(self, ftype='form', title='', instructions=''):
|
||||
return Form(self.xmpp, ftype, title, instructions)
|
||||
|
||||
class FieldContainer(object):
|
||||
def __init__(self, stanza = 'form'):
|
||||
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__')])
|
||||
self.field[xml.get('var', '__unnamed__')].buildField(xml)
|
||||
|
||||
def buildContainer(self, xml):
|
||||
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=''):
|
||||
if not ftype in self.types:
|
||||
raise ValueError("Invalid Form Type")
|
||||
FieldContainer.__init__(self)
|
||||
self.xmpp = xmpp
|
||||
self.type = ftype
|
||||
self.title = title
|
||||
self.instructions = instructions
|
||||
self.reported = []
|
||||
self.items = []
|
||||
|
||||
def merge(self, form2):
|
||||
form1 = Form(ftype=self.type)
|
||||
form1.fromXML(self.getXML(self.type))
|
||||
for field in form2.fields:
|
||||
if not field.var in form1.field:
|
||||
form1.addField(field.var, field.type, field.label, field.desc, field.required, field.value)
|
||||
else:
|
||||
form1.field[field.var].value = field.value
|
||||
for option, label in field.options:
|
||||
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:
|
||||
value = field.value
|
||||
if len(value) == 1:
|
||||
value = value[0]
|
||||
result[field.var] = value
|
||||
return result
|
||||
|
||||
def setValues(self, values={}):
|
||||
for field in values:
|
||||
if field in self.field:
|
||||
if isinstance(values[field], list) or isinstance(values[field], tuple):
|
||||
for value in values[field]:
|
||||
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)
|
||||
return newitem
|
||||
|
||||
def buildItem(self, xml):
|
||||
newitem = self.addItem()
|
||||
newitem.buildContainer(xml)
|
||||
|
||||
def addReported(self):
|
||||
reported = FieldContainer('reported')
|
||||
self.reported.append(reported)
|
||||
return reported
|
||||
|
||||
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:
|
||||
self.setTitle(xml.find('{jabber:x:data}title').text)
|
||||
if xml.find('{jabber:x:data}instructions') is not None:
|
||||
self.setInstructions(xml.find('{jabber:x:data}instructions').text)
|
||||
for field in xml.findall('{jabber:x:data}field'):
|
||||
self.buildField(field)
|
||||
for reported in xml.findall('{jabber:x:data}reported'):
|
||||
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:
|
||||
self.type = ftype
|
||||
form = ET.Element('{jabber:x:data}x')
|
||||
form.attrib['type'] = self.type
|
||||
if self.title and self.type in ('form', 'result'):
|
||||
title = ET.Element('{jabber:x:data}title')
|
||||
title.text = self.title
|
||||
form.append(title)
|
||||
if self.instructions and self.type == 'form':
|
||||
instructions = ET.Element('{jabber:x:data}instructions')
|
||||
instructions.text = self.instructions
|
||||
form.append(instructions)
|
||||
for field in self.fields:
|
||||
form.append(field.getXML(self.type))
|
||||
for reported in self.reported:
|
||||
form.append(reported.getXML('{jabber:x:data}reported'))
|
||||
for item in self.items:
|
||||
form.append(item.getXML(self.type))
|
||||
#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:
|
||||
title = ET.Element('h2')
|
||||
title.text = self.title
|
||||
form.append(title)
|
||||
if self.instructions:
|
||||
instructions = ET.Element('p')
|
||||
instructions.text = self.instructions
|
||||
form.append(instructions)
|
||||
for field in self.fields:
|
||||
form.append(field.getXHTML())
|
||||
for field in self.reported:
|
||||
form.append(field.getXHTML())
|
||||
for field in self.items:
|
||||
form.append(field.getXHTML())
|
||||
return form
|
||||
|
||||
|
||||
def makeSubmit(self):
|
||||
self.setType('submit')
|
||||
|
||||
class FormField(object):
|
||||
types = ('boolean', 'fixed', 'hidden', 'jid-multi', 'jid-single', 'list-multi', 'list-single', 'text-multi', 'text-private', 'text-single')
|
||||
listtypes = ('jid-multi', 'jid-single', 'list-multi', 'list-single')
|
||||
lbtypes = ('fixed', 'text-multi')
|
||||
def __init__(self, var, ftype='text-single', label='', desc='', required=False, value=None):
|
||||
if not ftype in self.types:
|
||||
raise ValueError("Invalid Field Type")
|
||||
self.type = ftype
|
||||
self.var = var
|
||||
self.label = label
|
||||
self.desc = desc
|
||||
self.options = []
|
||||
self.required = False
|
||||
self.value = []
|
||||
if self.type in self.listtypes:
|
||||
self.islist = True
|
||||
else:
|
||||
self.islist = False
|
||||
if self.type in self.lbtypes:
|
||||
self.islinebreak = True
|
||||
else:
|
||||
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]
|
||||
|
||||
def setFalse(self):
|
||||
if self.type == 'boolean':
|
||||
self.value = [False]
|
||||
|
||||
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'):
|
||||
value = True
|
||||
else:
|
||||
value = False
|
||||
if self.islinebreak and value is not None:
|
||||
self.value += value.split('\n')
|
||||
else:
|
||||
if len(self.value) and (not self.islist or self.type == 'list-single'):
|
||||
self.value = [value]
|
||||
else:
|
||||
self.value.append(value)
|
||||
|
||||
def delValue(self, value):
|
||||
if type(self.value) == type([]):
|
||||
try:
|
||||
idx = self.value.index(value)
|
||||
if idx != -1:
|
||||
self.value.pop(idx)
|
||||
except ValueError:
|
||||
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', '')
|
||||
for option in xml.findall('{jabber:x:data}option'):
|
||||
self.addOption(option.find('{jabber:x:data}value').text, option.get('label', ''))
|
||||
for value in xml.findall('{jabber:x:data}value'):
|
||||
self.setValue(value.text)
|
||||
if xml.find('{jabber:x:data}required') is not None:
|
||||
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':
|
||||
field.attrib['type'] = self.type
|
||||
if self.type != 'fixed':
|
||||
if self.var:
|
||||
field.attrib['var'] = self.var
|
||||
if self.label:
|
||||
field.attrib['label'] = self.label
|
||||
if ftype == 'form':
|
||||
for option in self.options:
|
||||
optionxml = ET.Element('{jabber:x:data}option')
|
||||
optionxml.attrib['label'] = option[1]
|
||||
optionval = ET.Element('{jabber:x:data}value')
|
||||
optionval.text = option[0]
|
||||
optionxml.append(optionval)
|
||||
field.append(optionxml)
|
||||
if self.required:
|
||||
required = ET.Element('{jabber:x:data}required')
|
||||
field.append(required)
|
||||
if self.desc:
|
||||
desc = ET.Element('{jabber:x:data}desc')
|
||||
desc.text = self.desc
|
||||
field.append(desc)
|
||||
for value in self.value:
|
||||
valuexml = ET.Element('{jabber:x:data}value')
|
||||
if value is True or value is False:
|
||||
if value:
|
||||
valuexml.text = '1'
|
||||
else:
|
||||
valuexml.text = '0'
|
||||
else:
|
||||
valuexml.text = value
|
||||
field.append(valuexml)
|
||||
return field
|
||||
|
||||
def getXHTML(self):
|
||||
field = ET.Element('div', {'class': 'xmpp-xforms-%s' % self.type})
|
||||
if self.label:
|
||||
label = ET.Element('p')
|
||||
label.text = "%s: " % self.label
|
||||
else:
|
||||
label = ET.Element('p')
|
||||
label.text = "%s: " % self.var
|
||||
field.append(label)
|
||||
if self.type == 'boolean':
|
||||
formf = ET.Element('input', {'type': 'checkbox', 'name': self.var})
|
||||
if len(self.value) and self.value[0] in (True, 'true', '1'):
|
||||
formf.attrib['checked'] = 'checked'
|
||||
elif self.type == 'fixed':
|
||||
formf = ET.Element('p')
|
||||
try:
|
||||
formf.text = ', '.join(self.value)
|
||||
except:
|
||||
pass
|
||||
field.append(formf)
|
||||
formf = ET.Element('input', {'type': 'hidden', 'name': self.var})
|
||||
try:
|
||||
formf.text = ', '.join(self.value)
|
||||
except:
|
||||
pass
|
||||
elif self.type == 'hidden':
|
||||
formf = ET.Element('input', {'type': 'hidden', 'name': self.var})
|
||||
try:
|
||||
formf.text = ', '.join(self.value)
|
||||
except:
|
||||
pass
|
||||
elif self.type in ('jid-multi', 'list-multi'):
|
||||
formf = ET.Element('select', {'name': self.var})
|
||||
for option in self.options:
|
||||
optf = ET.Element('option', {'value': option[0], 'multiple': 'multiple'})
|
||||
optf.text = option[1]
|
||||
if option[1] in self.value:
|
||||
optf.attrib['selected'] = 'selected'
|
||||
formf.append(option)
|
||||
elif self.type in ('jid-single', 'text-single'):
|
||||
formf = ET.Element('input', {'type': 'text', 'name': self.var})
|
||||
try:
|
||||
formf.attrib['value'] = ', '.join(self.value)
|
||||
except:
|
||||
pass
|
||||
elif self.type == 'list-single':
|
||||
formf = ET.Element('select', {'name': self.var})
|
||||
for option in self.options:
|
||||
optf = ET.Element('option', {'value': option[0]})
|
||||
optf.text = option[1]
|
||||
if not optf.text:
|
||||
optf.text = option[0]
|
||||
if option[1] in self.value:
|
||||
optf.attrib['selected'] = 'selected'
|
||||
formf.append(optf)
|
||||
elif self.type == 'text-multi':
|
||||
formf = ET.Element('textarea', {'name': self.var})
|
||||
try:
|
||||
formf.text = ', '.join(self.value)
|
||||
except:
|
||||
pass
|
||||
if not formf.text:
|
||||
formf.text = ' '
|
||||
elif self.type == 'text-private':
|
||||
formf = ET.Element('input', {'type': 'password', 'name': self.var})
|
||||
try:
|
||||
formf.attrib['value'] = ', '.join(self.value)
|
||||
except:
|
||||
pass
|
||||
label.append(formf)
|
||||
return field
|
||||
|
||||
def handle_form(self, message):
|
||||
self.xmpp.event("message_xform", message)
|
||||
|
||||
@@ -178,9 +178,12 @@ class xep_0009(base.base_plugin):
|
||||
def plugin_init(self):
|
||||
self.xep = '0009'
|
||||
self.description = 'Jabber-RPC'
|
||||
self.xmpp.add_handler("<iq type='set'><query xmlns='jabber:iq:rpc' /></iq>", self._callMethod)
|
||||
self.xmpp.add_handler("<iq type='result'><query xmlns='jabber:iq:rpc' /></iq>", self._callResult)
|
||||
self.xmpp.add_handler("<iq type='error'><query xmlns='jabber:iq:rpc' /></iq>", self._callError)
|
||||
self.xmpp.add_handler("<iq type='set'><query xmlns='jabber:iq:rpc' /></iq>",
|
||||
self._callMethod, name='Jabber RPC Call')
|
||||
self.xmpp.add_handler("<iq type='result'><query xmlns='jabber:iq:rpc' /></iq>",
|
||||
self._callResult, name='Jabber RPC Result')
|
||||
self.xmpp.add_handler("<iq type='error'><query xmlns='jabber:iq:rpc' /></iq>",
|
||||
self._callError, name='Jabber RPC Error')
|
||||
self.entries = {}
|
||||
self.activeCalls = []
|
||||
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
See the file license.txt for copying permissio
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from . import base
|
||||
from .. xmlstream.handler.callback import Callback
|
||||
from .. xmlstream.matcher.xpath import MatchXPath
|
||||
from .. xmlstream.stanzabase import ElementBase, ET, JID
|
||||
from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID
|
||||
from .. stanza.iq import Iq
|
||||
|
||||
class DiscoInfo(ElementBase):
|
||||
@@ -138,6 +138,9 @@ class DiscoNode(object):
|
||||
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
|
||||
@@ -201,8 +204,8 @@ class xep_0030(base.base_plugin):
|
||||
DiscoInfo.namespace)),
|
||||
self.handle_info_query))
|
||||
|
||||
self.xmpp.stanzaPlugin(Iq, DiscoInfo)
|
||||
self.xmpp.stanzaPlugin(Iq, DiscoItems)
|
||||
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)
|
||||
@@ -290,21 +293,21 @@ class xep_0030(base.base_plugin):
|
||||
|
||||
# Older interface methods for backwards compatibility
|
||||
|
||||
def getInfo(self, jid, node=''):
|
||||
def getInfo(self, jid, node='', dfrom=None):
|
||||
iq = self.xmpp.Iq()
|
||||
iq['type'] = 'get'
|
||||
iq['to'] = jid
|
||||
iq['from'] = self.xmpp.fulljid
|
||||
iq['from'] = dfrom
|
||||
iq['disco_info']['node'] = node
|
||||
iq.send()
|
||||
return iq.send()
|
||||
|
||||
def getItems(self, jid, node=''):
|
||||
def getItems(self, jid, node='', dfrom=None):
|
||||
iq = self.xmpp.Iq()
|
||||
iq['type'] = 'get'
|
||||
iq['to'] = jid
|
||||
iq['from'] = self.xmpp.fulljid
|
||||
iq['from'] = dfrom
|
||||
iq['disco_items']['node'] = node
|
||||
iq.send()
|
||||
return iq.send()
|
||||
|
||||
def add_feature(self, feature, node='main'):
|
||||
self.add_node(node)
|
||||
|
||||
161
sleekxmpp/plugins/xep_0033.py
Normal file
161
sleekxmpp/plugins/xep_0033.py
Normal file
@@ -0,0 +1,161 @@
|
||||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from . import base
|
||||
from .. xmlstream.handler.callback import Callback
|
||||
from .. xmlstream.matcher.xpath import MatchXPath
|
||||
from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID
|
||||
from .. stanza.message import Message
|
||||
|
||||
|
||||
class Addresses(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/address'
|
||||
name = 'addresses'
|
||||
plugin_attrib = 'addresses'
|
||||
interfaces = set(('addresses', 'bcc', 'cc', 'noreply', 'replyroom', 'replyto', 'to'))
|
||||
|
||||
def addAddress(self, atype='to', jid='', node='', uri='', desc='', delivered=False):
|
||||
address = Address(parent=self)
|
||||
address['type'] = atype
|
||||
address['jid'] = jid
|
||||
address['node'] = node
|
||||
address['uri'] = uri
|
||||
address['desc'] = desc
|
||||
address['delivered'] = delivered
|
||||
return address
|
||||
|
||||
def getAddresses(self, atype=None):
|
||||
addresses = []
|
||||
for addrXML in self.xml.findall('{%s}address' % Address.namespace):
|
||||
# ElementTree 1.2.6 does not support [@attr='value'] in findall
|
||||
if atype is None or addrXML.attrib.get('type') == atype:
|
||||
addresses.append(Address(xml=addrXML, parent=None))
|
||||
return addresses
|
||||
|
||||
def setAddresses(self, addresses, set_type=None):
|
||||
self.delAddresses(set_type)
|
||||
for addr in addresses:
|
||||
addr = dict(addr)
|
||||
# Remap 'type' to 'atype' to match the add method
|
||||
if set_type is not None:
|
||||
addr['type'] = set_type
|
||||
curr_type = addr.get('type', None)
|
||||
if curr_type is not None:
|
||||
del addr['type']
|
||||
addr['atype'] = curr_type
|
||||
self.addAddress(**addr)
|
||||
|
||||
def delAddresses(self, atype=None):
|
||||
if atype is None:
|
||||
return
|
||||
for addrXML in self.xml.findall('{%s}address' % Address.namespace):
|
||||
# ElementTree 1.2.6 does not support [@attr='value'] in findall
|
||||
if addrXML.attrib.get('type') == atype:
|
||||
self.xml.remove(addrXML)
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
def delBcc(self):
|
||||
self.delAddresses('bcc')
|
||||
|
||||
def delCc(self):
|
||||
self.delAddresses('cc')
|
||||
|
||||
def delNoreply(self):
|
||||
self.delAddresses('noreply')
|
||||
|
||||
def delReplyroom(self):
|
||||
self.delAddresses('replyroom')
|
||||
|
||||
def delReplyto(self):
|
||||
self.delAddresses('replyto')
|
||||
|
||||
def delTo(self):
|
||||
self.delAddresses('to')
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
def getBcc(self):
|
||||
return self.getAddresses('bcc')
|
||||
|
||||
def getCc(self):
|
||||
return self.getAddresses('cc')
|
||||
|
||||
def getNoreply(self):
|
||||
return self.getAddresses('noreply')
|
||||
|
||||
def getReplyroom(self):
|
||||
return self.getAddresses('replyroom')
|
||||
|
||||
def getReplyto(self):
|
||||
return self.getAddresses('replyto')
|
||||
|
||||
def getTo(self):
|
||||
return self.getAddresses('to')
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
def setBcc(self, addresses):
|
||||
self.setAddresses(addresses, 'bcc')
|
||||
|
||||
def setCc(self, addresses):
|
||||
self.setAddresses(addresses, 'cc')
|
||||
|
||||
def setNoreply(self, addresses):
|
||||
self.setAddresses(addresses, 'noreply')
|
||||
|
||||
def setReplyroom(self, addresses):
|
||||
self.setAddresses(addresses, 'replyroom')
|
||||
|
||||
def setReplyto(self, addresses):
|
||||
self.setAddresses(addresses, 'replyto')
|
||||
|
||||
def setTo(self, addresses):
|
||||
self.setAddresses(addresses, 'to')
|
||||
|
||||
|
||||
class Address(ElementBase):
|
||||
namespace = 'http://jabber.org/protocol/address'
|
||||
name = 'address'
|
||||
plugin_attrib = 'address'
|
||||
interfaces = set(('delivered', 'desc', 'jid', 'node', 'type', 'uri'))
|
||||
address_types = set(('bcc', 'cc', 'noreply', 'replyroom', 'replyto', 'to'))
|
||||
|
||||
def getDelivered(self):
|
||||
return self.xml.attrib.get('delivered', False)
|
||||
|
||||
def setDelivered(self, delivered):
|
||||
if delivered:
|
||||
self.xml.attrib['delivered'] = "true"
|
||||
else:
|
||||
del self['delivered']
|
||||
|
||||
def setUri(self, uri):
|
||||
if uri:
|
||||
del self['jid']
|
||||
del self['node']
|
||||
self.xml.attrib['uri'] = uri
|
||||
elif 'uri' in self.xml.attrib:
|
||||
del self.xml.attrib['uri']
|
||||
|
||||
|
||||
class xep_0030(base.base_plugin):
|
||||
"""
|
||||
XEP-0033: Extended Stanza Addressing
|
||||
"""
|
||||
|
||||
def plugin_init(self):
|
||||
self.xep = '0033'
|
||||
self.description = 'Extended Stanza Addressing'
|
||||
|
||||
registerStanzaPlugin(Message, Addresses)
|
||||
|
||||
def post_init(self):
|
||||
base.base_plugin.post_init(self)
|
||||
self.xmpp.plugin['xep_0030'].add_feature(Addresses.namespace)
|
||||
@@ -1,27 +1,15 @@
|
||||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2007 Nathanael C. Fritz
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
SleekXMPP is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
SleekXMPP is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with SleekXMPP; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
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
|
||||
from . import base
|
||||
import logging
|
||||
from xml.etree import cElementTree as ET
|
||||
from .. xmlstream.stanzabase import ElementBase, JID
|
||||
from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, JID
|
||||
from .. stanza.presence import Presence
|
||||
from .. xmlstream.handler.callback import Callback
|
||||
from .. xmlstream.matcher.xpath import MatchXPath
|
||||
@@ -125,7 +113,7 @@ class xep_0045(base.base_plugin):
|
||||
self.xep = '0045'
|
||||
self.description = 'Multi User Chat'
|
||||
# load MUC support in presence stanzas
|
||||
self.xmpp.stanzaPlugin(Presence, MUCPresence)
|
||||
registerStanzaPlugin(Presence, MUCPresence)
|
||||
self.xmpp.registerHandler(Callback('MUCPresence', MatchXMLMask("<presence xmlns='%s' />" % self.xmpp.default_ns), self.handle_groupchat_presence))
|
||||
self.xmpp.registerHandler(Callback('MUCMessage', MatchXMLMask("<message xmlns='%s' type='groupchat'><body/></message>" % self.xmpp.default_ns), self.handle_groupchat_message))
|
||||
|
||||
@@ -134,7 +122,7 @@ class xep_0045(base.base_plugin):
|
||||
"""
|
||||
if pr['muc']['room'] not in self.rooms.keys():
|
||||
return
|
||||
entry = pr['muc'].getValues()
|
||||
entry = pr['muc'].getStanzaValues()
|
||||
if pr['type'] == 'unavailable':
|
||||
del self.rooms[entry['room']][entry['nick']]
|
||||
else:
|
||||
@@ -166,13 +154,13 @@ class xep_0045(base.base_plugin):
|
||||
return False
|
||||
xform = result.xml.find('{http://jabber.org/protocol/muc#owner}query/{jabber:x:data}x')
|
||||
if xform is None: return False
|
||||
form = self.xmpp.plugin['xep_0004'].buildForm(xform)
|
||||
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['xep_0004'].makeForm(ftype='submit')
|
||||
#form = self.xmpp.plugin['old_0004'].makeForm(ftype='submit')
|
||||
#form.addField('FORM_TYPE', value='http://jabber.org/protocol/muc#roomconfig')
|
||||
iq = self.xmpp.makeIqSet()
|
||||
iq['to'] = room
|
||||
@@ -274,7 +262,7 @@ class xep_0045(base.base_plugin):
|
||||
form = result.xml.find('{http://jabber.org/protocol/muc#owner}query/{jabber:x:data}x')
|
||||
if form is None:
|
||||
raise ValueError
|
||||
return self.xmpp.plugin['xep_0004'].buildForm(form)
|
||||
return self.xmpp.plugin['old_0004'].buildForm(form)
|
||||
|
||||
def cancelConfig(self, room):
|
||||
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
|
||||
|
||||
@@ -1,27 +1,14 @@
|
||||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2007 Nathanael C. Fritz
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
SleekXMPP is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
SleekXMPP is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with SleekXMPP; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
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
|
||||
from . import base
|
||||
import logging
|
||||
from xml.etree import cElementTree as ET
|
||||
import traceback
|
||||
import time
|
||||
|
||||
class xep_0050(base.base_plugin):
|
||||
@@ -32,11 +19,11 @@ class xep_0050(base.base_plugin):
|
||||
def plugin_init(self):
|
||||
self.xep = '0050'
|
||||
self.description = 'Ad-Hoc Commands'
|
||||
self.xmpp.add_handler("<iq type='set' xmlns='%s'><command xmlns='http://jabber.org/protocol/commands' action='__None__'/></iq>" % self.xmpp.default_ns, self.handler_command)
|
||||
self.xmpp.add_handler("<iq type='set' xmlns='%s'><command xmlns='http://jabber.org/protocol/commands' action='execute'/></iq>" % self.xmpp.default_ns, self.handler_command)
|
||||
self.xmpp.add_handler("<iq type='set' xmlns='%s'><command xmlns='http://jabber.org/protocol/commands' action='next'/></iq>" % self.xmpp.default_ns, self.handler_command_next, threaded=True)
|
||||
self.xmpp.add_handler("<iq type='set' xmlns='%s'><command xmlns='http://jabber.org/protocol/commands' action='cancel'/></iq>" % self.xmpp.default_ns, self.handler_command_cancel)
|
||||
self.xmpp.add_handler("<iq type='set' xmlns='%s'><command xmlns='http://jabber.org/protocol/commands' action='complete'/></iq>" % self.xmpp.default_ns, self.handler_command_complete)
|
||||
self.xmpp.add_handler("<iq type='set' xmlns='%s'><command xmlns='http://jabber.org/protocol/commands' action='__None__'/></iq>" % self.xmpp.default_ns, self.handler_command, name='Ad-Hoc None')
|
||||
self.xmpp.add_handler("<iq type='set' xmlns='%s'><command xmlns='http://jabber.org/protocol/commands' action='execute'/></iq>" % self.xmpp.default_ns, self.handler_command, name='Ad-Hoc Execute')
|
||||
self.xmpp.add_handler("<iq type='set' xmlns='%s'><command xmlns='http://jabber.org/protocol/commands' action='next'/></iq>" % self.xmpp.default_ns, self.handler_command_next, name='Ad-Hoc Next', threaded=True)
|
||||
self.xmpp.add_handler("<iq type='set' xmlns='%s'><command xmlns='http://jabber.org/protocol/commands' action='cancel'/></iq>" % self.xmpp.default_ns, self.handler_command_cancel, name='Ad-Hoc Cancel')
|
||||
self.xmpp.add_handler("<iq type='set' xmlns='%s'><command xmlns='http://jabber.org/protocol/commands' action='complete'/></iq>" % self.xmpp.default_ns, self.handler_command_complete, name='Ad-Hoc Complete')
|
||||
self.commands = {}
|
||||
self.sessions = {}
|
||||
self.sd = self.xmpp.plugin['xep_0030']
|
||||
@@ -83,7 +70,7 @@ class xep_0050(base.base_plugin):
|
||||
in_command = xml.find('{http://jabber.org/protocol/commands}command')
|
||||
sessionid = in_command.get('sessionid', None)
|
||||
pointer = self.sessions[sessionid]['next']
|
||||
results = self.xmpp.plugin['xep_0004'].makeForm('result')
|
||||
results = self.xmpp.plugin['old_0004'].makeForm('result')
|
||||
results.fromXML(in_command.find('{jabber:x:data}x'))
|
||||
pointer(results,sessionid)
|
||||
self.xmpp.send(self.makeCommand(xml.attrib['from'], in_command.attrib['node'], form=None, id=xml.attrib['id'], sessionid=sessionid, status='completed', actions=[]))
|
||||
@@ -94,7 +81,7 @@ class xep_0050(base.base_plugin):
|
||||
in_command = xml.find('{http://jabber.org/protocol/commands}command')
|
||||
sessionid = in_command.get('sessionid', None)
|
||||
pointer = self.sessions[sessionid]['next']
|
||||
results = self.xmpp.plugin['xep_0004'].makeForm('result')
|
||||
results = self.xmpp.plugin['old_0004'].makeForm('result')
|
||||
results.fromXML(in_command.find('{jabber:x:data}x'))
|
||||
form, npointer, next = pointer(results,sessionid)
|
||||
self.sessions[sessionid]['next'] = npointer
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import with_statement
|
||||
from . import base
|
||||
import logging
|
||||
#from xml.etree import cElementTree as ET
|
||||
from .. xmlstream.stanzabase import ElementBase, ET
|
||||
from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET
|
||||
from . import stanza_pubsub
|
||||
|
||||
class xep_0060(base.base_plugin):
|
||||
|
||||
@@ -1,21 +1,9 @@
|
||||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2007 Nathanael C. Fritz
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
SleekXMPP is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
SleekXMPP is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with SleekXMPP; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
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
|
||||
from xml.etree import cElementTree as ET
|
||||
|
||||
101
sleekxmpp/plugins/xep_0085.py
Normal file
101
sleekxmpp/plugins/xep_0085.py
Normal file
@@ -0,0 +1,101 @@
|
||||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
See the file LICENSE for copying permissio
|
||||
"""
|
||||
|
||||
import logging
|
||||
from . import base
|
||||
from .. xmlstream.handler.callback import Callback
|
||||
from .. xmlstream.matcher.xpath import MatchXPath
|
||||
from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID
|
||||
from .. stanza.message import Message
|
||||
|
||||
|
||||
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')
|
||||
|
||||
def gone(self):
|
||||
self.setState('gone')
|
||||
|
||||
def inactive(self):
|
||||
self.setState('inactive')
|
||||
|
||||
def paused(self):
|
||||
self.setState('paused')
|
||||
|
||||
def setState(self, state):
|
||||
if state in self.states:
|
||||
self.name = state
|
||||
self.xml.tag = '{%s}%s' % (self.namespace, state)
|
||||
else:
|
||||
raise ValueError('Invalid chat state')
|
||||
|
||||
def getState(self):
|
||||
return self.name
|
||||
|
||||
# In order to match the various chat state elements,
|
||||
# we need one stanza object per state, even though
|
||||
# they are all the same except for the initial name
|
||||
# value. Do not depend on the type of the chat state
|
||||
# stanza object for the actual state.
|
||||
|
||||
class Active(ChatState):
|
||||
name = 'active'
|
||||
class Composing(ChatState):
|
||||
name = 'composing'
|
||||
class Gone(ChatState):
|
||||
name = 'gone'
|
||||
class Inactive(ChatState):
|
||||
name = 'inactive'
|
||||
class Paused(ChatState):
|
||||
name = 'paused'
|
||||
|
||||
|
||||
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'),
|
||||
('Inactive Chat State', 'inactive'),
|
||||
('Paused Chat State', 'paused')]
|
||||
for handler in handlers:
|
||||
self.xmpp.registerHandler(
|
||||
Callback(handler[0],
|
||||
MatchXPath("{%s}message/{%s}%s" % (self.xmpp.default_ns,
|
||||
ChatState.namespace,
|
||||
handler[1])),
|
||||
self._handleChatState))
|
||||
|
||||
registerStanzaPlugin(Message, Active)
|
||||
registerStanzaPlugin(Message, Composing)
|
||||
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))
|
||||
self.xmpp.event('chatstate_%s' % state, msg)
|
||||
@@ -1,21 +1,9 @@
|
||||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2007 Nathanael C. Fritz
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
SleekXMPP is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
SleekXMPP is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with SleekXMPP; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
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
|
||||
from . import base
|
||||
@@ -30,7 +18,7 @@ class xep_0092(base.base_plugin):
|
||||
self.xep = "0092"
|
||||
self.name = self.config.get('name', 'SleekXMPP')
|
||||
self.version = self.config.get('version', '0.1-dev')
|
||||
self.xmpp.add_handler("<iq type='get' xmlns='%s'><query xmlns='jabber:iq:version' /></iq>" % self.xmpp.default_ns, self.report_version)
|
||||
self.xmpp.add_handler("<iq type='get' xmlns='%s'><query xmlns='jabber:iq:version' /></iq>" % self.xmpp.default_ns, self.report_version, name='Sofware Version')
|
||||
|
||||
def post_init(self):
|
||||
base.base_plugin.post_init(self)
|
||||
|
||||
51
sleekxmpp/plugins/xep_0128.py
Normal file
51
sleekxmpp/plugins/xep_0128.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
See the file LICENSE for copying permission.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from . import base
|
||||
from .. xmlstream.handler.callback import Callback
|
||||
from .. xmlstream.matcher.xpath import MatchXPath
|
||||
from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID
|
||||
from .. stanza.iq import Iq
|
||||
from . xep_0030 import DiscoInfo, DiscoItems
|
||||
from . xep_0004 import Form
|
||||
|
||||
|
||||
class xep_0128(base.base_plugin):
|
||||
"""
|
||||
XEP-0128 Service Discovery Extensions
|
||||
"""
|
||||
|
||||
def plugin_init(self):
|
||||
self.xep = '0128'
|
||||
self.description = 'Service Discovery Extensions'
|
||||
|
||||
registerStanzaPlugin(DiscoInfo, Form)
|
||||
registerStanzaPlugin(DiscoItems, Form)
|
||||
|
||||
def extend_info(self, node, data=None):
|
||||
if data is None:
|
||||
data = {}
|
||||
node = self.xmpp['xep_0030'].nodes.get(node, None)
|
||||
if node is None:
|
||||
self.xmpp['xep_0030'].add_node(node)
|
||||
|
||||
info = node.info
|
||||
info['form']['type'] = 'result'
|
||||
info['form'].setFields(data, default=None)
|
||||
|
||||
def extend_items(self, node, data=None):
|
||||
if data is None:
|
||||
data = {}
|
||||
node = self.xmpp['xep_0030'].nodes.get(node, None)
|
||||
if node is None:
|
||||
self.xmpp['xep_0030'].add_node(node)
|
||||
|
||||
items = node.items
|
||||
items['form']['type'] = 'result'
|
||||
items['form'].setFields(data, default=None)
|
||||
@@ -1,22 +1,9 @@
|
||||
"""
|
||||
SleekXMPP: The Sleek XMPP Library
|
||||
XEP-0199 (Ping) support
|
||||
Copyright (C) 2007 Kevin Smith
|
||||
This file is part of SleekXMPP.
|
||||
|
||||
SleekXMPP is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
SleekXMPP is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with SleekXMPP; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
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
|
||||
from . import base
|
||||
@@ -29,7 +16,7 @@ class xep_0199(base.base_plugin):
|
||||
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)
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user