Mercurial > salut
annotate salut.py @ 2:77b77f48c975
don't expect queries to specify all the fields
author | souliane <souliane@mailoo.org> |
---|---|
date | Thu, 23 Jul 2015 21:05:51 +0200 |
parents | 92549e4336a6 |
children | 593345584d21 |
rev | line source |
---|---|
0 | 1 #!/usr/bin/env python2 |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # SAT plugin for account creation (experimental) | |
1 | 5 # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Jérôme Poisson (goffi@goffi.org) |
0 | 6 |
7 # This program is free software: you can redistribute it and/or modify | |
8 # it under the terms of the GNU Affero General Public License as published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (at your option) any later version. | |
11 | |
12 # This program is distributed in the hope that it will be useful, | |
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 # GNU Affero General Public License for more details. | |
16 | |
17 # You should have received a copy of the GNU Affero General Public License | |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | |
20 from twisted.words.xish import domish | |
21 from wokkel.subprotocols import XMPPHandler, IQHandlerMixin | |
22 from wokkel import disco, iwokkel, data_form | |
23 from twisted.words.protocols.jabber import jid | |
24 from zope.interface import implements | |
25 import uuid | |
26 import sqlite3 | |
27 from os import path | |
28 from collections import OrderedDict | |
29 import gettext | |
30 gettext.install('sat', "i18n", unicode=True) | |
31 | |
32 DATABASE = "salut.db" | |
33 ID_CMD_LIST = disco.DiscoIdentity("automation", "command-list") | |
34 NS_COMMANDS = "http://jabber.org/protocol/commands" | |
35 NS_SEARCH = 'jabber:iq:search' | |
36 QUERY_SEARCH = "/query[@xmlns='jabber:iq:search']" | |
37 SUBSCRIBE_CMD = "/iq[@type='set']/command[@xmlns='"+NS_COMMANDS+"' and @node='subscribe']" | |
38 UNSUBSCRIBE_CMD = "/iq[@type='set']/command[@xmlns='"+NS_COMMANDS+"' and @node='unsubscribe']" | |
39 INSTRUCTIONS = _(u'This is a minimal directory for the Libervia demo (Salut à Toi project). For testing purpose only.') | |
40 FORM_INSTRUCTIONS = [INSTRUCTIONS] | |
41 | |
42 SUBSCRIBE_TXT = _(u"Please give some words about you, then submit to be registered on the directory") | |
43 SUBSCRIBE_DONE = _("You are now registered on the directory") | |
44 UNSUBSCRIBE_DONE = _("You have been removed from the directory") | |
45 DB_CREATE = ['PRAGMA user_version=0', | |
46 'CREATE TABLE directory (jid TEXT PRIMARY KEY, description TEXT)'] | |
47 | |
48 class SalutGateway(XMPPHandler, IQHandlerMixin): | |
49 implements(iwokkel.IDisco) | |
50 | |
51 def __init__(self, component=False): | |
52 XMPPHandler.__init__(self) | |
53 IQHandlerMixin.__init__(self) | |
54 self.component = component | |
55 self.discoHandler = disco.DiscoHandler() | |
56 new_db = not path.exists(DATABASE) | |
57 conn = sqlite3.connect(DATABASE) | |
58 self.db = conn.cursor() # we use SQLite in a blocking way, performance is not such a big deal here | |
59 if new_db: | |
60 for statement in DB_CREATE: | |
61 self.db.execute(statement) | |
62 | |
63 def connectionMade(self): | |
64 print "Connected!" | |
65 self.xmlstream.addObserver("/iq[@type='get']" + QUERY_SEARCH, self.handleFieldsRequest) | |
66 self.xmlstream.addObserver("/iq[@type='set']" + QUERY_SEARCH, self.handleSearchRequest) | |
67 self.xmlstream.addObserver(SUBSCRIBE_CMD, self.onSubscribe) | |
68 self.xmlstream.addObserver(UNSUBSCRIBE_CMD, self.onUnsubscribe) | |
69 self.discoHandler.setHandlerParent(self.parent) | |
70 | |
71 def connectionLost(self, reason): | |
72 print "Disconnected!" | |
73 | |
74 def handleFieldsRequest(self, request): | |
75 result = domish.Element((None, 'iq')) | |
76 result['type'] = 'result' | |
77 result['id'] = request['id'] | |
78 result['to'] = request['from'] | |
79 query_elt = result.addElement('query', NS_SEARCH) | |
80 instructions_elt = query_elt.addElement('instructions', content=INSTRUCTIONS) | |
81 form = data_form.Form('form', title=_('Directory search'), | |
82 instructions=FORM_INSTRUCTIONS, | |
83 formNamespace=NS_SEARCH) | |
84 form.addField(data_form.Field('fixed', label=_('Enter part of description or jid to find somebody,'))) | |
85 form.addField(data_form.Field('fixed', label=('let empty to have a full list of people'))) | |
86 form.addField(data_form.Field('text-single', 'jid', label=_('jid'))) | |
87 form.addField(data_form.Field('text-single', 'description', label=_('Description'))) | |
88 query_elt.addChild(form.toElement()) | |
89 self.xmlstream.send(result) | |
90 | |
91 def handleSearchRequest(self, request): | |
92 query = ["SELECT jid, description FROM directory"] | |
93 args = OrderedDict() | |
94 try: | |
95 query_elt = request.elements(NS_SEARCH, 'query').next() | |
96 form_elt = query_elt.elements(data_form.NS_X_DATA, 'x').next() | |
2
77b77f48c975
don't expect queries to specify all the fields
souliane <souliane@mailoo.org>
parents:
1
diff
changeset
|
97 except StopIteration: |
77b77f48c975
don't expect queries to specify all the fields
souliane <souliane@mailoo.org>
parents:
1
diff
changeset
|
98 raise ValueError # TODO: proper error handling |
77b77f48c975
don't expect queries to specify all the fields
souliane <souliane@mailoo.org>
parents:
1
diff
changeset
|
99 else: |
0 | 100 parsed_form = data_form.Form.fromElement(form_elt) |
101 for col in ('jid', 'description'): | |
2
77b77f48c975
don't expect queries to specify all the fields
souliane <souliane@mailoo.org>
parents:
1
diff
changeset
|
102 try: |
77b77f48c975
don't expect queries to specify all the fields
souliane <souliane@mailoo.org>
parents:
1
diff
changeset
|
103 value = parsed_form[col].strip() |
77b77f48c975
don't expect queries to specify all the fields
souliane <souliane@mailoo.org>
parents:
1
diff
changeset
|
104 except KeyError: |
77b77f48c975
don't expect queries to specify all the fields
souliane <souliane@mailoo.org>
parents:
1
diff
changeset
|
105 pass |
77b77f48c975
don't expect queries to specify all the fields
souliane <souliane@mailoo.org>
parents:
1
diff
changeset
|
106 else: |
77b77f48c975
don't expect queries to specify all the fields
souliane <souliane@mailoo.org>
parents:
1
diff
changeset
|
107 if value: |
77b77f48c975
don't expect queries to specify all the fields
souliane <souliane@mailoo.org>
parents:
1
diff
changeset
|
108 args[col] = value |
0 | 109 |
110 if args: | |
111 query.append("WHERE") | |
112 query.append(" AND ".join(("%s LIKE ?" % col for col in args))) | |
113 | |
114 row_iter = self.db.execute(' '.join(query), tuple(['%'+arg+'%' for arg in args.values()])) | |
115 | |
116 result = domish.Element((None, 'iq')) | |
117 result['type'] = 'result' | |
118 result['id'] = request['id'] | |
119 result['to'] = request['from'] | |
120 query_elt = result.addElement('query', NS_SEARCH) | |
121 x_form = data_form.Form('result', formNamespace = NS_SEARCH) | |
122 x_form_elt = x_form.toElement() | |
123 reported_elt = x_form_elt.addElement('reported') | |
124 jid_field_elt = reported_elt.addElement('field') | |
125 jid_field_elt['label'] = 'Jabber ID' | |
126 jid_field_elt['var'] = 'jid' | |
127 description_field_elt = reported_elt.addElement('field') | |
128 description_field_elt['label'] = 'Description' | |
129 description_field_elt['var'] = 'description' | |
130 for row in row_iter: | |
131 for col, value in zip(('jid', 'description'), row): | |
132 item_elt = x_form_elt.addElement('item') | |
133 field_elt = item_elt.addElement('field') | |
134 field_elt['var'] = col | |
135 value_elt = field_elt.addElement('value', content=value) | |
136 | |
137 query_elt.addChild(x_form_elt) | |
138 self.xmlstream.send(result) | |
139 | |
140 def onSubscribe(self, request): | |
141 result = domish.Element((None, 'iq')) | |
142 result['type'] = 'result' | |
143 result['id'] = request['id'] | |
144 result['to'] = request['from'] | |
145 from_ = jid.JID(request['from']) | |
146 request_cmd = request.elements(NS_COMMANDS, 'command').next() | |
147 command_elt = result.addElement('command', NS_COMMANDS) | |
148 try: | |
149 session_id = request_cmd['sessionid'] | |
150 except KeyError: | |
151 session_id = None | |
152 | |
153 if session_id is None: | |
154 # first request, we send the form | |
155 command_elt['status'] = 'executing' | |
156 session_id = str(uuid.uuid4()) | |
157 actions_elt = command_elt.addElement('actions') | |
158 actions_elt['execute'] = 'next' | |
159 actions_elt.addElement('next') | |
160 form = data_form.Form('form', instructions=FORM_INSTRUCTIONS, title=_('Directory subscription')) | |
161 infos = data_form.Field('fixed', value=SUBSCRIBE_TXT) | |
162 desc = data_form.Field('text-single', 'description', label=_(u"Some words about you")) | |
163 form.addField(infos) | |
164 form.addField(desc) | |
165 command_elt.addChild(form.toElement()) | |
166 else: | |
167 req_forms = request_cmd.elements(data_form.NS_X_DATA, 'x') | |
168 try: | |
169 req_form = req_forms.next() | |
170 parsed_form = data_form.Form.fromElement(req_form) | |
171 description = parsed_form['description'] | |
172 except (StopIteration, KeyError): | |
173 raise ValueError # TODO: properly cancel the command | |
174 self.db.execute('REPLACE INTO directory(jid,description) VALUES (?,?)', (from_.userhost(), description)) | |
175 self.db.connection.commit() | |
176 command_elt['status'] = 'completed' | |
177 note_elt = command_elt.addElement('note') | |
178 note_elt['type'] = 'info' | |
179 note_elt.addContent(SUBSCRIBE_DONE) | |
180 command_elt['sessionid'] = session_id | |
181 command_elt['node'] = request_cmd['node'] | |
182 self.xmlstream.send(result) | |
183 | |
184 def onUnsubscribe(self, request): | |
185 result = domish.Element((None, 'iq')) | |
186 result['type'] = 'result' | |
187 result['id'] = request['id'] | |
188 result['to'] = request['from'] | |
189 from_ = jid.JID(request['from']) | |
190 request_cmd = request.elements(NS_COMMANDS, 'command').next() | |
191 command_elt = result.addElement('command', NS_COMMANDS) | |
192 self.db.execute('DELETE FROM directory WHERE jid=?', (from_.userhost(),)) | |
193 self.db.connection.commit() | |
194 command_elt['status'] = 'completed' | |
195 note_elt = command_elt.addElement('note') | |
196 note_elt['type'] = 'info' | |
197 note_elt.addContent(UNSUBSCRIBE_DONE) | |
198 command_elt['node'] = request_cmd['node'] | |
199 self.xmlstream.send(result) | |
200 | |
201 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | |
202 return [disco.DiscoFeature(NS_SEARCH), | |
203 disco.DiscoIdentity(u"directory", u"user", u"salut"), | |
204 disco.DiscoFeature(NS_COMMANDS), | |
205 ID_CMD_LIST] | |
206 | |
207 def getDiscoItems(self, requestor, target, nodeIdentifier=''): | |
208 ret = [] | |
209 if nodeIdentifier == NS_COMMANDS: | |
210 ret.append(disco.DiscoItem(target, "subscribe", "Subscribe to the directory")) | |
211 ret.append(disco.DiscoItem(target, "unsubscribe", "Unsubscribe from the directory")) | |
212 return ret |