Mercurial > salut
view salut.py @ 0:d1bc50b64974
initial commit
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 25 Feb 2014 22:22:18 +0100 |
parents | |
children | 92549e4336a6 |
line wrap: on
line source
#!/usr/bin/env python2 # -*- coding: utf-8 -*- # SAT plugin for account creation (experimental) # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Jérôme Poisson (goffi@goffi.org) # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program 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 Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from twisted.words.xish import domish from wokkel.subprotocols import XMPPHandler, IQHandlerMixin from wokkel import disco, iwokkel, data_form from twisted.words.protocols.jabber import jid from zope.interface import implements import uuid import sqlite3 from os import path from collections import OrderedDict import gettext gettext.install('sat', "i18n", unicode=True) DATABASE = "salut.db" ID_CMD_LIST = disco.DiscoIdentity("automation", "command-list") NS_COMMANDS = "http://jabber.org/protocol/commands" NS_SEARCH = 'jabber:iq:search' QUERY_SEARCH = "/query[@xmlns='jabber:iq:search']" SUBSCRIBE_CMD = "/iq[@type='set']/command[@xmlns='"+NS_COMMANDS+"' and @node='subscribe']" UNSUBSCRIBE_CMD = "/iq[@type='set']/command[@xmlns='"+NS_COMMANDS+"' and @node='unsubscribe']" INSTRUCTIONS = _(u'This is a minimal directory for the Libervia demo (Salut à Toi project). For testing purpose only.') FORM_INSTRUCTIONS = [INSTRUCTIONS] SUBSCRIBE_TXT = _(u"Please give some words about you, then submit to be registered on the directory") SUBSCRIBE_DONE = _("You are now registered on the directory") UNSUBSCRIBE_DONE = _("You have been removed from the directory") DB_CREATE = ['PRAGMA user_version=0', 'CREATE TABLE directory (jid TEXT PRIMARY KEY, description TEXT)'] class SalutGateway(XMPPHandler, IQHandlerMixin): implements(iwokkel.IDisco) def __init__(self, component=False): XMPPHandler.__init__(self) IQHandlerMixin.__init__(self) self.component = component self.discoHandler = disco.DiscoHandler() new_db = not path.exists(DATABASE) conn = sqlite3.connect(DATABASE) self.db = conn.cursor() # we use SQLite in a blocking way, performance is not such a big deal here if new_db: for statement in DB_CREATE: self.db.execute(statement) def connectionMade(self): print "Connected!" self.xmlstream.addObserver("/iq[@type='get']" + QUERY_SEARCH, self.handleFieldsRequest) self.xmlstream.addObserver("/iq[@type='set']" + QUERY_SEARCH, self.handleSearchRequest) self.xmlstream.addObserver(SUBSCRIBE_CMD, self.onSubscribe) self.xmlstream.addObserver(UNSUBSCRIBE_CMD, self.onUnsubscribe) self.discoHandler.setHandlerParent(self.parent) def connectionLost(self, reason): print "Disconnected!" def handleFieldsRequest(self, request): result = domish.Element((None, 'iq')) result['type'] = 'result' result['id'] = request['id'] result['to'] = request['from'] query_elt = result.addElement('query', NS_SEARCH) instructions_elt = query_elt.addElement('instructions', content=INSTRUCTIONS) form = data_form.Form('form', title=_('Directory search'), instructions=FORM_INSTRUCTIONS, formNamespace=NS_SEARCH) form.addField(data_form.Field('fixed', label=_('Enter part of description or jid to find somebody,'))) form.addField(data_form.Field('fixed', label=('let empty to have a full list of people'))) form.addField(data_form.Field('text-single', 'jid', label=_('jid'))) form.addField(data_form.Field('text-single', 'description', label=_('Description'))) query_elt.addChild(form.toElement()) self.xmlstream.send(result) def handleSearchRequest(self, request): query = ["SELECT jid, description FROM directory"] args = OrderedDict() try: query_elt = request.elements(NS_SEARCH, 'query').next() form_elt = query_elt.elements(data_form.NS_X_DATA, 'x').next() parsed_form = data_form.Form.fromElement(form_elt) for col in ('jid', 'description'): value = parsed_form[col].strip() if value: args[col] = value except (StopIteration, KeyError): raise ValueError # TODO: proper error handling if args: query.append("WHERE") query.append(" AND ".join(("%s LIKE ?" % col for col in args))) row_iter = self.db.execute(' '.join(query), tuple(['%'+arg+'%' for arg in args.values()])) result = domish.Element((None, 'iq')) result['type'] = 'result' result['id'] = request['id'] result['to'] = request['from'] query_elt = result.addElement('query', NS_SEARCH) x_form = data_form.Form('result', formNamespace = NS_SEARCH) x_form_elt = x_form.toElement() reported_elt = x_form_elt.addElement('reported') jid_field_elt = reported_elt.addElement('field') jid_field_elt['label'] = 'Jabber ID' jid_field_elt['var'] = 'jid' description_field_elt = reported_elt.addElement('field') description_field_elt['label'] = 'Description' description_field_elt['var'] = 'description' for row in row_iter: for col, value in zip(('jid', 'description'), row): item_elt = x_form_elt.addElement('item') field_elt = item_elt.addElement('field') field_elt['var'] = col value_elt = field_elt.addElement('value', content=value) query_elt.addChild(x_form_elt) self.xmlstream.send(result) def onSubscribe(self, request): result = domish.Element((None, 'iq')) result['type'] = 'result' result['id'] = request['id'] result['to'] = request['from'] from_ = jid.JID(request['from']) request_cmd = request.elements(NS_COMMANDS, 'command').next() command_elt = result.addElement('command', NS_COMMANDS) try: session_id = request_cmd['sessionid'] except KeyError: session_id = None if session_id is None: # first request, we send the form command_elt['status'] = 'executing' session_id = str(uuid.uuid4()) actions_elt = command_elt.addElement('actions') actions_elt['execute'] = 'next' actions_elt.addElement('next') form = data_form.Form('form', instructions=FORM_INSTRUCTIONS, title=_('Directory subscription')) infos = data_form.Field('fixed', value=SUBSCRIBE_TXT) desc = data_form.Field('text-single', 'description', label=_(u"Some words about you")) form.addField(infos) form.addField(desc) command_elt.addChild(form.toElement()) else: req_forms = request_cmd.elements(data_form.NS_X_DATA, 'x') try: req_form = req_forms.next() parsed_form = data_form.Form.fromElement(req_form) description = parsed_form['description'] except (StopIteration, KeyError): raise ValueError # TODO: properly cancel the command self.db.execute('REPLACE INTO directory(jid,description) VALUES (?,?)', (from_.userhost(), description)) self.db.connection.commit() command_elt['status'] = 'completed' note_elt = command_elt.addElement('note') note_elt['type'] = 'info' note_elt.addContent(SUBSCRIBE_DONE) command_elt['sessionid'] = session_id command_elt['node'] = request_cmd['node'] self.xmlstream.send(result) def onUnsubscribe(self, request): result = domish.Element((None, 'iq')) result['type'] = 'result' result['id'] = request['id'] result['to'] = request['from'] from_ = jid.JID(request['from']) request_cmd = request.elements(NS_COMMANDS, 'command').next() command_elt = result.addElement('command', NS_COMMANDS) self.db.execute('DELETE FROM directory WHERE jid=?', (from_.userhost(),)) self.db.connection.commit() command_elt['status'] = 'completed' note_elt = command_elt.addElement('note') note_elt['type'] = 'info' note_elt.addContent(UNSUBSCRIBE_DONE) command_elt['node'] = request_cmd['node'] self.xmlstream.send(result) def getDiscoInfo(self, requestor, target, nodeIdentifier=''): return [disco.DiscoFeature(NS_SEARCH), disco.DiscoIdentity(u"directory", u"user", u"salut"), disco.DiscoFeature(NS_COMMANDS), ID_CMD_LIST] def getDiscoItems(self, requestor, target, nodeIdentifier=''): ret = [] if nodeIdentifier == NS_COMMANDS: ret.append(disco.DiscoItem(target, "subscribe", "Subscribe to the directory")) ret.append(disco.DiscoItem(target, "unsubscribe", "Unsubscribe from the directory")) return ret