moved seesmic branch to trunk

This commit is contained in:
Nathan Fritz
2009-06-03 22:56:51 +00:00
commit 96b103b275
44 changed files with 4041 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
"""
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
"""
__all__ = ['xep_0004', 'xep_0030', 'xep_0045', 'xep_0050', 'xep_0078', 'xep_0092', 'xep_0199', 'gmail_notify', 'xep_0060']

35
sleekxmpp/plugins/base.py Normal file
View File

@@ -0,0 +1,35 @@
"""
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
"""
class base_plugin(object):
def __init__(self, xmpp, config):
self.xep = 'base'
self.description = 'Base Plugin'
self.xmpp = xmpp
self.config = config
self.enable = config.get('enable', True)
if self.enable:
self.plugin_init()
def plugin_init(self):
pass
def post_init(self):
pass

View File

@@ -0,0 +1,57 @@
"""
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
"""
from __future__ import with_statement
import base
import logging
from xml.etree import cElementTree as ET
import traceback
import time
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 = self.xmpp.send(iq, self.xmpp.makeIq(self.xmpp.id))
mailbox = emails.find('{google:mail:notify}mailbox')
total = int(mailbox.get('total-matched', 0))
logging.info("%s New Gmail Messages" % total)

View File

@@ -0,0 +1,389 @@
"""
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
"""
from . import base
import logging
from xml.etree import cElementTree as ET
import copy
#TODO support item groups and results
class xep_0004(base.base_plugin):
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)
def post_init(self):
self.xmpp['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(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 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 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):
logging.debug("creating form as %s" % ftype)
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('title')
title.text = self.title
form.append(title)
if self.instructions and self.type == 'form':
instructions = ET.Element('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('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.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('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('option')
optionxml.attrib['label'] = option[1]
optionval = ET.Element('value')
optionval.text = option[0]
optionxml.append(optionval)
field.append(optionxml)
if self.required:
required = ET.Element('required')
field.append(required)
if self.desc:
desc = ET.Element('desc')
desc.text = self.desc
field.append(desc)
for value in self.value:
valuexml = ET.Element('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

View File

@@ -0,0 +1,273 @@
"""
XEP-0009 XMPP Remote Procedure Calls
"""
from __future__ import with_statement
import base
import logging
from xml.etree import cElementTree as ET
import copy
import time
import base64
def py2xml(*args):
params = ET.Element("params")
for x in args:
param = ET.Element("param")
param.append(_py2xml(x))
params.append(param) #<params><param>...
return params
def _py2xml(*args):
for x in args:
val = ET.Element("value")
if type(x) is int:
i4 = ET.Element("i4")
i4.text = str(x)
val.append(i4)
if type(x) is bool:
boolean = ET.Element("boolean")
boolean.text = str(int(x))
val.append(boolean)
elif type(x) is str:
string = ET.Element("string")
string.text = x
val.append(string)
elif type(x) is float:
double = ET.Element("double")
double.text = str(x)
val.append(double)
elif type(x) is rpcbase64:
b64 = ET.Element("Base64")
b64.text = x.encoded()
val.append(b64)
elif type(x) is rpctime:
iso = ET.Element("dateTime.iso8601")
iso.text = str(x)
val.append(iso)
elif type(x) is list:
array = ET.Element("array")
data = ET.Element("data")
for y in x:
data.append(_py2xml(y))
array.append(data)
val.append(array)
elif type(x) is dict:
struct = ET.Element("struct")
for y in x.keys():
member = ET.Element("member")
name = ET.Element("name")
name.text = y
member.append(name)
member.append(_py2xml(x[y]))
struct.append(member)
val.append(struct)
return val
def xml2py(params):
vals = []
for param in params.findall('param'):
vals.append(_xml2py(param.find('value')))
return vals
def _xml2py(value):
if value.find('i4') is not None:
return int(value.find('i4').text)
if value.find('int') is not None:
return int(value.find('int').text)
if value.find('boolean') is not None:
return bool(value.find('boolean').text)
if value.find('string') is not None:
return value.find('string').text
if value.find('double') is not None:
return float(value.find('double').text)
if value.find('Base64') is not None:
return rpcbase64(value.find('Base64').text)
if value.find('dateTime.iso8601') is not None:
return rpctime(value.find('dateTime.iso8601'))
if value.find('struct') is not None:
struct = {}
for member in value.find('struct').findall('member'):
struct[member.find('name').text] = _xml2py(member.find('value'))
return struct
if value.find('array') is not None:
array = []
for val in value.find('array').find('data').findall('value'):
array.append(_xml2py(val))
return array
raise ValueError()
class rpcbase64(object):
def __init__(self, data):
#base 64 encoded string
self.data = data
def decode(self):
return base64.decodestring(data)
def __str__(self):
return self.decode()
def encoded(self):
return self.data
class rpctime(object):
def __init__(self,data=None):
#assume string data is in iso format YYYYMMDDTHH:MM:SS
if type(data) is str:
self.timestamp = time.strptime(data,"%Y%m%dT%H:%M:%S")
elif type(data) is time.struct_time:
self.timestamp = data
elif data is None:
self.timestamp = time.gmtime()
else:
raise ValueError()
def iso8601(self):
#return a iso8601 string
return time.strftime("%Y%m%dT%H:%M:%S",self.timestamp)
def __str__(self):
return self.iso8601()
class JabberRPCEntry(object):
def __init__(self,call):
self.call = call
self.result = None
self.error = None
self.allow = {} #{'<jid>':['<resource1>',...],...}
self.deny = {}
def check_acl(self, jid, resource):
#Check for deny
if jid in self.deny.keys():
if self.deny[jid] == None or resource in self.deny[jid]:
return False
#Check for allow
if allow == None:
return True
if jid in self.allow.keys():
if self.allow[jid] == None or resource in self.allow[jid]:
return True
return False
def acl_allow(self, jid, resource):
if jid == None:
self.allow = None
elif resource == None:
self.allow[jid] = None
elif jid in self.allow.keys():
self.allow[jid].append(resource)
else:
self.allow[jid] = [resource]
def acl_deny(self, jid, resource):
if jid == None:
self.deny = None
elif resource == None:
self.deny[jid] = None
elif jid in self.deny.keys():
self.deny[jid].append(resource)
else:
self.deny[jid] = [resource]
def call_method(self, args):
ret = self.call(*args)
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.entries = {}
self.activeCalls = []
def post_init(self):
self.xmpp['xep_0030'].add_feature('jabber:iq:rpc')
self.xmpp['xep_0030'].add_identity('automatition','rpc')
def register_call(self, method, name=None):
#@returns an string that can be used in acl commands.
with self.lock:
if name is None:
self.entries[method.__name__] = JabberRPCEntry(method)
return method.__name__
else:
self.entries[name] = JabberRPCEntry(method)
return name
def acl_allow(self, entry, jid=None, resource=None):
#allow the method entry to be called by the given jid and resource.
#if jid is None it will allow any jid/resource.
#if resource is None it will allow any resource belonging to the jid.
with self.lock:
if self.entries[entry]:
self.entries[entry].acl_allow(jid,resource)
else:
raise ValueError()
def acl_deny(self, entry, jid=None, resource=None):
#Note: by default all requests are denied unless allowed with acl_allow.
#If you deny an entry it will not be allowed regardless of acl_allow
with self.lock:
if self.entries[entry]:
self.entries[entry].acl_deny(jid,resource)
else:
raise ValueError()
def unregister_call(self, entry):
#removes the registered call
with self.lock:
if self.entries[entry]:
del self.entries[entry]
else:
raise ValueError()
def makeMethodCallQuery(self,pmethod,params):
query = self.xmpp.makeIqQuery(iq,"jabber:iq:rpc")
methodCall = ET.Element('methodCall')
methodName = ET.Element('methodName')
methodName.text = pmethod
methodCall.append(methodName)
methodCall.append(params)
query.append(methodCall)
return query
def makeIqMethodCall(self,pto,pmethod,params):
iq = self.xmpp.makeIqSet()
iq.set('to',pto)
iq.append(self.makeMethodCallQuery(pmethod,params))
return iq
def makeIqMethodResponse(self,pto,pid,params):
iq = self.xmpp.makeIqResult(pid)
iq.set('to',pto)
query = self.xmpp.makeIqQuery(iq,"jabber:iq:rpc")
methodResponse = ET.Element('methodResponse')
methodResponse.append(params)
query.append(methodResponse)
return iq
def makeIqMethodError(self,pto,id,pmethod,params,condition):
iq = self.xmpp.makeIqError(id)
iq.set('to',pto)
iq.append(self.makeMethodCallQuery(pmethod,params))
iq.append(self.xmpp['xep_0086'].makeError(condition))
return iq
def call_remote(self, pto, pmethod, *args):
#calls a remote method. Returns the id of the Iq.
pass
def _callMethod(self,xml):
pass
def _callResult(self,xml):
pass
def _callError(self,xml):
pass

View File

@@ -0,0 +1,117 @@
"""
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
"""
from __future__ import absolute_import, with_statement
from . import base
import logging
from xml.etree import cElementTree as ET
import thread
class xep_0030(base.base_plugin):
"""
XEP-0030 Service Discovery
"""
def plugin_init(self):
self.xep = '0030'
self.description = 'Service Discovery'
self.features = {'main': ['http://jabber.org/protocol/disco#info', 'http://jabber.org/protocol/disco#items']}
self.identities = {'main': [{'category': 'client', 'type': 'pc', 'name': 'SleekXMPP'}]}
self.items = {'main': []}
self.xmpp.add_handler("<iq type='get' xmlns='%s'><query xmlns='http://jabber.org/protocol/disco#info' /></iq>" % self.xmpp.default_ns, self.info_handler)
self.xmpp.add_handler("<iq type='get' xmlns='%s'><query xmlns='http://jabber.org/protocol/disco#items' /></iq>" % self.xmpp.default_ns, self.item_handler)
self.lock = thread.allocate_lock()
def add_feature(self, feature, node='main'):
with self.lock:
if not self.features.has_key(node):
self.features[node] = []
self.features[node].append(feature)
def add_identity(self, category=None, itype=None, name=None, node='main'):
if not self.identities.has_key(node):
self.identities[node] = []
self.identities[node].append({'category': category, 'type': itype, 'name': name})
def add_item(self, jid=None, name=None, node='main', subnode=''):
if not self.items.has_key(node):
self.items[node] = []
self.items[node].append({'jid': jid, 'name': name, 'node': subnode})
def info_handler(self, xml):
logging.debug("Info request from %s" % xml.get('from', ''))
iq = self.xmpp.makeIqResult(xml.get('id', self.xmpp.getNewId()))
iq.attrib['from'] = self.xmpp.fulljid
iq.attrib['to'] = xml.get('from', self.xmpp.server)
query = xml.find('{http://jabber.org/protocol/disco#info}query')
node = query.get('node', 'main')
for identity in self.identities.get(node, []):
idxml = ET.Element('identity')
for attrib in identity:
if identity[attrib]:
idxml.attrib[attrib] = identity[attrib]
query.append(idxml)
for feature in self.features.get(node, []):
featxml = ET.Element('feature')
featxml.attrib['var'] = feature
query.append(featxml)
iq.append(query)
#print ET.tostring(iq)
self.xmpp.send(iq)
def item_handler(self, xml):
logging.debug("Item request from %s" % xml.get('from', ''))
iq = self.xmpp.makeIqResult(xml.get('id', self.xmpp.getNewId()))
iq.attrib['from'] = self.xmpp.fulljid
iq.attrib['to'] = xml.get('from', self.xmpp.server)
query = self.xmpp.makeIqQuery(iq, 'http://jabber.org/protocol/disco#items').find('{http://jabber.org/protocol/disco#items}query')
node = xml.find('{http://jabber.org/protocol/disco#items}query').get('node', 'main')
for item in self.items.get(node, []):
itemxml = ET.Element('item')
itemxml.attrib = item
if itemxml.attrib['jid'] is None:
itemxml.attrib['jid'] = self.xmpp.fulljid
query.append(itemxml)
self.xmpp.send(iq)
def getItems(self, jid, node=None):
iq = self.xmpp.makeIqGet()
iq.attrib['from'] = self.xmpp.fulljid
iq.attrib['to'] = jid
self.xmpp.makeIqQuery(iq, 'http://jabber.org/protocol/disco#items')
if node:
iq.find('{http://jabber.org/protocol/disco#items}query').attrib['node'] = node
return self.xmpp.send(iq, "<iq id='%s' />" % iq.get('id'))
def getInfo(self, jid, node=None):
iq = self.xmpp.makeIqGet()
iq.attrib['from'] = self.xmpp.fulljid
iq.attrib['to'] = jid
self.xmpp.makeIqQuery(iq, 'http://jabber.org/protocol/disco#info')
if node:
iq.find('{http://jabber.org/protocol/disco#info}query').attrib['node'] = node
return self.xmpp.send(iq, self.xmpp.makeIq(iq.get('id')))
def parseInfo(self, xml):
result = {'identity': {}, 'feature': []}
for identity in xml.findall('{http://jabber.org/protocol/disco#info}query/{{http://jabber.org/protocol/disco#info}identity'):
result['identity'][identity['name']] = identity.attrib
for feature in xml.findall('{http://jabber.org/protocol/disco#info}query/{{http://jabber.org/protocol/disco#info}feature'):
result['feature'].append(feature.get('var', '__unknown__'))
return result

View File

@@ -0,0 +1,193 @@
"""
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
"""
from __future__ import with_statement
import base
import logging
from xml.etree import cElementTree as ET
class xep_0045(base.base_plugin):
"""
Impliments XEP-0045 Multi User Chat
"""
def plugin_init(self):
self.rooms = {}
self.ourNicks = {}
self.xep = '0045'
self.description = 'Multi User Chat (Very Basic Still)'
self.xmpp.add_handler("<message xmlns='%s' type='groupchat'><body/></message>" % self.xmpp.default_ns, self.handle_groupchat_message)
self.xmpp.add_handler("<presence />", self.handle_groupchat_presence)
def handle_groupchat_presence(self, xml):
""" Handle a presence in a muc.
"""
source = xml.attrib['from']
room = self.xmpp.getjidbare(source)
if room not in self.rooms.keys():
return
nick = self.xmpp.getjidresource(source)
entry = {
'nick': nick,
'room': room,
}
if 'type' in xml.attrib.keys():
entry['type'] = xml.attrib['type']
for tag in ['status','show','priority']:
if xml.find(('{%s}' % self.xmpp.default_ns) + tag) != None:
entry[tag] = xml.find(('{%s}' % self.xmpp.default_ns) + tag).text
else:
entry[tag] = None
for tag in ['affiliation','role','jid']:
item = xml.find('{http://jabber.org/protocol/muc#user}x/{http://jabber.org/protocol/muc#user}item')
if item != None:
if tag in item.attrib:
entry[tag] = item.attrib[tag]
else:
entry[tag] = None
else:
entry[tag] = None
if entry['status'] == 'unavailable':
self.rooms[room][nick] = None
else:
self.rooms[room][nick] = entry
logging.debug("MUC presence from %s/%s : %s" % (entry['room'],entry['nick'], entry))
self.xmpp.event("groupchat_presence", entry)
def handle_groupchat_message(self, xml):
""" Handle a message event in a muc.
"""
mfrom = xml.attrib['from']
message = xml.find('{%s}body' % self.xmpp.default_ns).text
subject = xml.find('{%s}subject' % self.xmpp.default_ns)
if subject:
subject = subject.text
else:
subject = ''
resource = self.xmpp.getjidresource(mfrom)
mfrom = self.xmpp.getjidbare(mfrom)
mtype = xml.attrib.get('type', 'normal')
self.xmpp.event("groupchat_message", {'room': mfrom, 'name': resource, 'type': mtype, 'subject': subject, 'message': message})
def joinMUC(self, room, nick, maxhistory="0", password='', wait=False):
""" Join the specified room, requesting 'maxhistory' lines of history.
"""
stanza = self.xmpp.makePresence(pto="%s/%s" % (room, nick))
x = ET.Element('{http://jabber.org/protocol/muc}x')
if password:
passelement = ET.Element('password')
passelement.text = password
x.append(passelement)
history = ET.Element('history')
history.attrib['maxstanzas'] = maxhistory
x.append(history)
stanza.append(x)
if not wait:
self.xmpp.send(stanza)
else:
#wait for our own room presence back
expect = ET.Element('{jabber:client}presence', {'from':"%s/%s" % (room, nick)})
self.xmpp.send(stanza, expect)
self.rooms[room] = {}
self.ourNicks[room] = nick
def setAffiliation(self, room, jid, affiliation='member'):
""" Change room affiliation."""
if affiliation not in ('outcast', 'member', 'admin', 'owner', 'none'):
raise TypeError
query = ET.Element('{http://jabber.org/protocol/muc#admin}query')
item = ET.Element('item', {'affiliation':affiliation, 'jid':jid})
query.append(item)
iq = self.xmpp.makeIqSet(query)
iq.attrib['to'] = room
result = self.xmpp.send(iq, "<iq id='%s' />" % iq.get('id'))
if result is None or result.get('type') != 'result':
raise ValueError
return True
def invite(self, room, jid, reason=''):
""" Invite a jid to a room."""
msg = self.xmpp.makeMessage(room, mtype='none')
x = ET.Element('{http://jabber.org/protocol/muc#user}x')
invite = ET.Element('invite', {'to': jid})
if reason:
rxml = ET.Element('reason')
rxml.text = reason
invite.append(rxml)
x.append(invite)
msg.append(x)
self.xmpp.send(msg)
def leaveMUC(self, room, nick):
""" Leave the specified room.
"""
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.attrib['to'] = room
result = self.xmpp.send(iq, "<iq id='%s' />" % iq.get('id'))
if result is None or result.get('type') != 'result':
raise ValueError
form = result.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)
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)
self.xmpp.send(iq, "<iq id='%s' />" % iq.get('id'))
def setRoomConfig(self, room, config):
query = ET.Element('{http://jabber.org/protocol/muc#owner}query')
x = config.getXML('submit')
query.append(x)
iq = self.xmpp.makeIqSet(query)
iq.attrib['to'] = room
self.xmpp.send(iq, "<iq id='%s' />" % iq.get('id'))
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.
"""
if self.rooms.has_key(room) and self.rooms[room].has_key(nick) and self.rooms[room][nick].has_key(jidProperty):
return self.rooms[room][nick][jidProperty]
else:
return None
def getRoster(self, room):
""" Get the list of nicks in a room.
"""
if room not in self.rooms.keys():
return None
return self.rooms[room].keys()

View File

@@ -0,0 +1,142 @@
"""
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
"""
from __future__ import with_statement
import base
import logging
from xml.etree import cElementTree as ET
import traceback
import time
import thread
class xep_0050(base.base_plugin):
"""
XEP-0050 Ad-Hoc Commands
"""
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.commands = {}
self.sessions = {}
def post_init(self):
self.xmpp['xep_0030'].add_feature('http://jabber.org/protocol/commands')
def addCommand(self, node, name, form, pointer=None, multi=False):
self.xmpp['xep_0030'].add_item(None, name, 'http://jabber.org/protocol/commands', node)
self.xmpp['xep_0030'].add_identity('automation', 'command-node', name, node)
self.xmpp['xep_0030'].add_feature('http://jabber.org/protocol/commands', node)
self.xmpp['xep_0030'].add_feature('jabber:x:data', node)
self.commands[node] = (name, form, pointer, multi)
def getNewSession(self):
return str(time.time()) + '-' + self.xmpp.getNewId()
def handler_command(self, xml):
in_command = xml.find('{http://jabber.org/protocol/commands}command')
sessionid = in_command.get('sessionid', None)
node = in_command.get('node')
sessionid = self.getNewSession()
name, form, pointer, multi = self.commands[node]
self.sessions[sessionid] = {}
self.sessions[sessionid]['jid'] = xml.get('from')
self.sessions[sessionid]['past'] = [(form, None)]
self.sessions[sessionid]['next'] = pointer
npointer = pointer
if multi:
actions = ['next']
status = 'executing'
else:
if pointer is None:
status = 'completed'
actions = []
else:
status = 'executing'
actions = ['complete']
self.xmpp.send(self.makeCommand(xml.attrib['from'], in_command.attrib['node'], form=form, id=xml.attrib['id'], sessionid=sessionid, status=status, actions=actions))
def handler_command_complete(self, xml):
in_command = xml.find('{http://jabber.org/protocol/commands}command')
sessionid = in_command.get('sessionid', None)
pointer = self.sessions[sessionid]['next']
results = self.xmpp['xep_0004'].makeForm('result')
results.fromXML(in_command.find('{jabber:x:data}x'))
apply(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=[]))
del self.sessions[command.get('sessionid')]
def handler_command_next(self, xml):
in_command = xml.find('{http://jabber.org/protocol/commands}command')
sessionid = in_command.get('sessionid', None)
pointer = self.sessions[sessionid]['next']
results = self.xmpp['xep_0004'].makeForm('result')
results.fromXML(in_command.find('{jabber:x:data}x'))
form, npointer, next = apply(pointer, (results,sessionid))
self.sessions[sessionid]['next'] = npointer
self.sessions[sessionid]['past'].append((form, pointer))
actions = []
actions.append('prev')
if npointer is None:
status = 'completed'
else:
status = 'executing'
if next:
actions.append('next')
else:
actions.append('complete')
self.xmpp.send(self.makeCommand(xml.attrib['from'], in_command.attrib['node'], form=form, id=xml.attrib['id'], sessionid=sessionid, status=status, actions=actions))
def handler_command_cancel(self, xml):
command = xml.find('{http://jabber.org/protocol/commands}command')
try:
del self.sessions[command.get('sessionid')]
except:
pass
self.xmpp.send(self.makeCommand(xml.attrib['from'], command.attrib['node'], id=xml.attrib['id'], sessionid=command.attrib['sessionid'], status='canceled'))
def makeCommand(self, to, node, id=None, form=None, sessionid=None, status='executing', actions=[]):
if not id:
id = self.xmpp.getNewId()
iq = self.xmpp.makeIqResult(id)
iq.attrib['from'] = self.xmpp.fulljid
iq.attrib['to'] = to
command = ET.Element('{http://jabber.org/protocol/commands}command')
command.attrib['node'] = node
command.attrib['status'] = status
xmlactions = ET.Element('actions')
for action in actions:
xmlactions.append(ET.Element(action))
if xmlactions:
command.append(xmlactions)
if not sessionid:
sessionid = self.getNewSession()
command.attrib['sessionid'] = sessionid
if form is not None:
if hasattr(form,'getXML'):
form = form.getXML()
command.append(form)
iq.append(command)
return iq

View File

@@ -0,0 +1,306 @@
from __future__ import with_statement
from . import base
import logging
from xml.etree import cElementTree as ET
class xep_0060(base.base_plugin):
"""
XEP-0060 Publish Subscribe
"""
def plugin_init(self):
self.xep = '0060'
self.description = 'Publish-Subscribe'
def create_node(self, jid, node, config=None, collection=False):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub')
create = ET.Element('create')
create.set('node', node)
pubsub.append(create)
configure = ET.Element('configure')
if config is None:
submitform = self.xmpp.plugin['xep_0004'].makeForm('submit')
else:
submitform = config
if 'FORM_TYPE' in submitform.field:
submitform.field['FORM_TYPE'].setValue('http://jabber.org/protocol/pubsub#node_config')
else:
submitform.addField('FORM_TYPE', 'hidden', value='http://jabber.org/protocol/pubsub#node_config')
if collection:
if 'pubsub#node_type' in submitform.field:
submitform.field['pubsub#node_type'].setValue('collection')
else:
submitform.addField('pubsub#node_type', value='collection')
else:
if 'pubsub#node_type' in submitform.field:
submitform.field['pubsub#node_type'].setValue('leaf')
else:
submitform.addField('pubsub#node_type', value='leaf')
configure.append(submitform.getXML('submit'))
pubsub.append(configure)
iq = self.xmpp.makeIqSet(pubsub)
iq.attrib['to'] = jid
iq.attrib['from'] = self.xmpp.fulljid
id = iq.get('id')
result = self.xmpp.send(iq, "<iq id='%s'/>" % id)
if result is False or result is None or result.get('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')
subscribe.attrib['node'] = node
if subscribee is None:
if bare:
subscribe.attrib['jid'] = self.xmpp.jid
else:
subscribe.attrib['jid'] = self.xmpp.fulljid
else:
subscribe.attrib['jid'] = subscribee
pubsub.append(subscribe)
iq = self.xmpp.makeIqSet(pubsub)
iq.attrib['to'] = jid
iq.attrib['from'] = self.xmpp.fulljid
id = iq.get('id')
result = self.xmpp.send(iq, "<iq id='%s'/>" % id)
if result is False or result is None or result.get('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')
unsubscribe.attrib['node'] = node
if subscribee is None:
if bare:
unsubscribe.attrib['jid'] = self.xmpp.jid
else:
unsubscribe.attrib['jid'] = self.xmpp.fulljid
else:
unsubscribe.attrib['jid'] = subscribee
pubsub.append(unsubscribe)
iq = self.xmpp.makeIqSet(pubsub)
iq.attrib['to'] = jid
iq.attrib['from'] = self.xmpp.fulljid
id = iq.get('id')
result = self.xmpp.send(iq, "<iq id='%s'/>" % id)
if result is False or result is None or result.get('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:
configure = ET.Element('configure')
configure.attrib['node'] = node
else:
configure = ET.Element('default')
pubsub.append(configure)
#TODO: Add configure support.
iq = self.xmpp.makeIqGet()
iq.append(pubsub)
iq.attrib['to'] = jid
iq.attrib['from'] = self.xmpp.fulljid
id = iq.get('id')
#self.xmpp.add_handler("<iq id='%s'/>" % id, self.handlerCreateNodeResponse)
result = self.xmpp.send(iq, "<iq id='%s'/>" % id)
if result is None or result == False or result.get('type') == 'error':
logging.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.")
return False
return self.xmpp.plugin['xep_0004'].buildForm(form)
def getNodeSubscriptions(self, jid, node):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub')
subscriptions = ET.Element('subscriptions')
subscriptions.attrib['node'] = node
pubsub.append(subscriptions)
iq = self.xmpp.makeIqGet()
iq.append(pubsub)
iq.attrib['to'] = jid
iq.attrib['from'] = self.xmpp.fulljid
id = iq.get('id')
result = self.xmpp.send(iq, "<iq id='%s'/>" % id)
if result is None or result == False or result.get('type') == 'error':
logging.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')
if results is None:
return False
subs = {}
for sub in results:
subs[sub.get('jid')] = sub.get('subscription')
return subs
def getNodeAffiliations(self, jid, node):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub')
affiliations = ET.Element('affiliations')
affiliations.attrib['node'] = node
pubsub.append(affiliations)
iq = self.xmpp.makeIqGet()
iq.append(pubsub)
iq.attrib['to'] = jid
iq.attrib['from'] = self.xmpp.fulljid
id = iq.get('id')
result = self.xmpp.send(iq, "<iq id='%s'/>" % id)
if result is None or result == False or result.get('type') == 'error':
logging.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')
if results is None:
return False
subs = {}
for sub in results:
subs[sub.get('jid')] = sub.get('affiliation')
return subs
def deleteNode(self, jid, node):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub')
iq = self.xmpp.makeIqSet()
delete = ET.Element('delete')
delete.attrib['node'] = node
pubsub.append(delete)
iq.append(pubsub)
iq.attrib['to'] = jid
iq.attrib['from'] = self.xmpp.fulljid
id = iq.get('id')
result = self.xmpp.send(iq, "<iq id='%s'/>" % id)
if result is not None and result is not False and result.attrib.get('type', 'error') != 'error':
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')
configure.attrib['node'] = node
config = config.getXML('submit')
configure.append(config)
pubsub.append(configure)
iq = self.xmpp.makeIqSet(pubsub)
iq.attrib['to'] = jid
iq.attrib['from'] = self.xmpp.fulljid
id = iq.get('id')
result = self.xmpp.send(iq, "<iq id='%s'/>" % id)
if result is None or result.get('type') == 'error':
print "---------- returning false, apparently"
return False
return True
def setItem(self, jid, node, items=[]):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub')
publish = ET.Element('publish')
publish.attrib['node'] = node
for pub_item in items:
id, payload = pub_item
item = ET.Element('item')
if id is not None:
item.attrib['id'] = id
item.append(payload)
publish.append(item)
pubsub.append(publish)
iq = self.xmpp.makeIqSet(pubsub)
iq.attrib['to'] = jid
iq.attrib['from'] = self.xmpp.fulljid
id = iq.get('id')
result = self.xmpp.send(iq, "<iq id='%s'/>" % id)
if result is None or result is False or result.get('type') == 'error': return False
return True
def deleteItem(self, jid, node, item):
pubsub = ET.Element('{http://jabber.org/protocol/pubsub}pubsub')
retract = ET.Element('retract')
retract.attrib['node'] = node
itemn = ET.Element('item')
itemn.attrib['id'] = item
retract.append(itemn)
pubsub.append(retract)
iq = self.xmpp.makeIqSet(pubsub)
iq.attrib['to'] = jid
iq.attrib['from'] = self.xmpp.fulljid
id = iq.get('id')
result = self.xmpp.send(iq, "<iq id='%s'/>" % id)
if result is None or result is False or result.get('type') == 'error': return False
return True
def addItem(self, jid, node, items=[]):
return setItem(jid, node, items)
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')
nodes = {}
if items is not None and items is not False:
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')
nodeitems = []
if items is not None and items is not False:
for item in items:
nodeitems.append(item.get('node'))
return nodeitems
def addNodeToCollection(self, jid, child, parent=''):
config = self.getNodeConfig(jid, child)
if not config or config is None:
self.lasterror = "Config Error"
return False
try:
config.field['pubsub#collection'].setValue(parent)
except KeyError:
logging.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
return True
def modifyAffiliation(self, ps_jid, node, user_jid, affiliation):
if affiliation not in ('owner', 'publisher', 'member', 'none', 'outcast'):
raise TypeError
pubsub = ET.Element('{http://jabber.org/protocol/pubsub#owner}pubsub')
affs = ET.Element('affiliations')
affs.attrib['node'] = node
aff = ET.Element('affiliation')
aff.attrib['jid'] = user_jid
aff.attrib['affiliation'] = affiliation
affs.append(aff)
pubsub.append(affs)
iq = self.xmpp.makeIqSet(pubsub)
iq.attrib['to'] = ps_jid
iq.attrib['from'] = self.xmpp.fulljid
id = iq.get('id')
result = self.xmpp.send(iq, "<iq id='%s'/>" % id)
if result is None or result is False or result.get('type') == 'error':
return False
return True
def addNodeToCollection(self, jid, child, parent=''):
config = self.getNodeConfig(jid, child)
if not config or config is None:
self.lasterror = "Config Error"
return False
try:
config.field['pubsub#collection'].setValue(parent)
except KeyError:
logging.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
return True
def removeNodeFromCollection(self, jid, child):
self.addNodeToCollection(jid, child, '')

View File

@@ -0,0 +1,81 @@
"""
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
"""
from __future__ import with_statement
from xml.etree import cElementTree as ET
import logging
import sha
import base
class xep_0078(base.base_plugin):
"""
XEP-0078 NON-SASL Authentication
"""
def plugin_init(self):
self.description = "Non-SASL Authentication (broken)"
self.xep = "0078"
self.xmpp.add_start_handler(self.check_stream)
#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")
auth_request = self.xmpp.makeIqGet()
auth_request_query = ET.Element('{jabber:iq:auth}query')
auth_request.attrib['to'] = self.xmpp.server
username = ET.Element('username')
username.text = self.xmpp.username
auth_request_query.append(username)
auth_request.append(auth_request_query)
result = self.xmpp.send(auth_request, self.xmpp.makeIqResult(self.xmpp.id))
rquery = result.find('{jabber:iq:auth}query')
attempt = self.xmpp.makeIqSet()
query = ET.Element('{jabber:iq:auth}query')
resource = ET.Element('resource')
resource.text = self.xmpp.resource
query.append(username)
query.append(resource)
if rquery.find('{jabber:iq:auth}digest') is None:
logging.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")
digest = ET.Element('digest')
digest.text = sha.sha("%s%s" % (self.streamid, self.xmpp.password)).hexdigest()
query.append(digest)
attempt.append(query)
result = self.xmpp.send(attempt, self.xmpp.makeIq(self.xmpp.id))
if result.attrib['type'] == 'result':
with self.xmpp.lock:
self.xmpp.authenticated = True
self.xmpp.sessionstarted = True
self.xmpp.event("session_start")
else:
logging.info("Authentication failed")
self.xmpp.disconnect()
self.xmpp.event("failed_auth")

View File

@@ -0,0 +1,49 @@
from __future__ import with_statement
import base
import logging
from xml.etree import cElementTree as ET
import copy
class xep_0086(base.base_plugin):
"""
XEP-0086 Error Condition Mappings
"""
def plugin_init(self):
self.xep = '0086'
self.description = 'Error Condition Mappings'
self.error_map = {
'bad-request':('modify','400'),
'conflict':('cancel','409'),
'feature-not-implemented':('cancel','501'),
'forbidden':('auth','403'),
'gone':('modify','302'),
'internal-server-error':('wait','500'),
'item-not-found':('cancel','404'),
'jid-malformed':('modify','400'),
'not-acceptable':('modify','406'),
'not-allowed':('cancel','405'),
'not-authorized':('auth','401'),
'payment-required':('auth','402'),
'recipient-unavailable':('wait','404'),
'redirect':('modify','302'),
'registration-required':('auth','407'),
'remote-server-not-found':('cancel','404'),
'remote-server-timeout':('wait','504'),
'resource-constraint':('wait','500'),
'service-unavailable':('cancel','503'),
'subscription-required':('auth','407'),
'undefined-condition':(None,'500'),
'unexpected-request':('wait','400')
}
def makeError(self, condition, cdata=None, errorType=None, text=None, customElem=None):
conditionElem = self.xmpp.makeStanzaErrorCondition(condition, cdata)
if errorType is None:
error = self.xmpp.makeStanzaError(conditionElem, self.error_map[condition][0], self.error_map[condition][1], text, customElem)
else:
error = self.xmpp.makeStanzaError(conditionElem, errorType, self.error_map[condition][1], text, customElem)
error.append(conditionElem)
return error

View File

@@ -0,0 +1,67 @@
"""
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
"""
from xml.etree import cElementTree as ET
from . import base
from .. xmlstream.handler.xmlwaiter import XMLWaiter
class xep_0092(base.base_plugin):
"""
XEP-0092 Software Version
"""
def plugin_init(self):
self.description = "Software Version"
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)
def post_init(self):
self.xmpp['xep_0030'].add_feature('jabber:iq:version')
def report_version(self, xml):
iq = self.xmpp.makeIqResult(xml.get('id', 'unknown'))
iq.attrib['to'] = xml.get('from', self.xmpp.server)
query = ET.Element('{jabber:iq:version}query')
name = ET.Element('name')
name.text = self.name
version = ET.Element('version')
version.text = self.version
query.append(name)
query.append(version)
iq.append(query)
self.xmpp.send(iq)
def getVersion(self, jid):
iq = self.xmpp.makeIqGet()
query = ET.Element('{jabber:iq:version}query')
iq.append(query)
iq.attrib['to'] = jid
iq.attrib['from'] = self.xmpp.fulljid
id = iq.get('id')
result = self.xmpp.send(iq, "<iq xmlns='%s' id='%s'/>" % (self.xmpp.default_ns, id))
if result and result is not None and result.get('type', 'error') != 'error':
qry = result.find('{jabber:iq:version}query')
version = {}
for child in qry.getchildren():
version[child.tag.split('}')[-1]] = child.text
return version
else:
return False

View File

@@ -0,0 +1,70 @@
"""
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
"""
from xml.etree import cElementTree as ET
from . import base
import time
import logging
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)
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):
self.xmpp['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)
endTime = time.clock()
if pingresult == False:
#self.xmpp.disconnect(reconnect=True)
return False
return endTime - startTime