Mercurial > salut
comparison salut.py @ 3:593345584d21 default tip
replace ad-hoc commands "subscribe" and "unsubscribe" with a single "update" command
author | souliane <souliane@mailoo.org> |
---|---|
date | Wed, 02 Sep 2015 14:02:50 +0200 |
parents | 77b77f48c975 |
children |
comparison
equal
deleted
inserted
replaced
2:77b77f48c975 | 3:593345584d21 |
---|---|
1 #!/usr/bin/env python2 | 1 #!/usr/bin/env python2 |
2 # -*- coding: utf-8 -*- | 2 # -*- coding: utf-8 -*- |
3 | 3 |
4 # SAT plugin for account creation (experimental) | 4 # SAT plugin for account creation (experimental) |
5 # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Jérôme Poisson (goffi@goffi.org) | 5 # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Jérôme Poisson (goffi@goffi.org) |
6 # Copyright (C) 2015 Adrien Cossa (souliane@mailoo.org) | |
6 | 7 |
7 # This program is free software: you can redistribute it and/or modify | 8 # 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 # 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 # the Free Software Foundation, either version 3 of the License, or |
10 # (at your option) any later version. | 11 # (at your option) any later version. |
32 DATABASE = "salut.db" | 33 DATABASE = "salut.db" |
33 ID_CMD_LIST = disco.DiscoIdentity("automation", "command-list") | 34 ID_CMD_LIST = disco.DiscoIdentity("automation", "command-list") |
34 NS_COMMANDS = "http://jabber.org/protocol/commands" | 35 NS_COMMANDS = "http://jabber.org/protocol/commands" |
35 NS_SEARCH = 'jabber:iq:search' | 36 NS_SEARCH = 'jabber:iq:search' |
36 QUERY_SEARCH = "/query[@xmlns='jabber:iq:search']" | 37 QUERY_SEARCH = "/query[@xmlns='jabber:iq:search']" |
37 SUBSCRIBE_CMD = "/iq[@type='set']/command[@xmlns='"+NS_COMMANDS+"' and @node='subscribe']" | 38 UPDATE_CMD = "/iq[@type='set']/command[@xmlns='"+NS_COMMANDS+"' and @node='update']" |
38 UNSUBSCRIBE_CMD = "/iq[@type='set']/command[@xmlns='"+NS_COMMANDS+"' and @node='unsubscribe']" | 39 INSTRUCTIONS = _(u'Update your subscription to the Jabber search directory.') |
39 INSTRUCTIONS = _(u'This is a minimal directory for the Libervia demo (Salut à Toi project). For testing purpose only.') | |
40 FORM_INSTRUCTIONS = [INSTRUCTIONS] | 40 FORM_INSTRUCTIONS = [INSTRUCTIONS] |
41 | 41 SUBSCRIBE_DONE = _("You registration to the directory has been updated.") |
42 SUBSCRIBE_TXT = _(u"Please give some words about you, then submit to be registered on the directory") | 42 UNSUBSCRIBE_DONE = _("You have been removed from 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', | 43 DB_CREATE = ['PRAGMA user_version=0', |
46 'CREATE TABLE directory (jid TEXT PRIMARY KEY, description TEXT)'] | 44 'CREATE TABLE directory (jid TEXT PRIMARY KEY, description TEXT)'] |
45 | |
47 | 46 |
48 class SalutGateway(XMPPHandler, IQHandlerMixin): | 47 class SalutGateway(XMPPHandler, IQHandlerMixin): |
49 implements(iwokkel.IDisco) | 48 implements(iwokkel.IDisco) |
50 | 49 |
51 def __init__(self, component=False): | 50 def __init__(self, component=False): |
62 | 61 |
63 def connectionMade(self): | 62 def connectionMade(self): |
64 print "Connected!" | 63 print "Connected!" |
65 self.xmlstream.addObserver("/iq[@type='get']" + QUERY_SEARCH, self.handleFieldsRequest) | 64 self.xmlstream.addObserver("/iq[@type='get']" + QUERY_SEARCH, self.handleFieldsRequest) |
66 self.xmlstream.addObserver("/iq[@type='set']" + QUERY_SEARCH, self.handleSearchRequest) | 65 self.xmlstream.addObserver("/iq[@type='set']" + QUERY_SEARCH, self.handleSearchRequest) |
67 self.xmlstream.addObserver(SUBSCRIBE_CMD, self.onSubscribe) | 66 self.xmlstream.addObserver(UPDATE_CMD, self.onUpdate) |
68 self.xmlstream.addObserver(UNSUBSCRIBE_CMD, self.onUnsubscribe) | |
69 self.discoHandler.setHandlerParent(self.parent) | 67 self.discoHandler.setHandlerParent(self.parent) |
70 | 68 |
71 def connectionLost(self, reason): | 69 def connectionLost(self, reason): |
72 print "Disconnected!" | 70 print "Disconnected!" |
73 | 71 |
74 def handleFieldsRequest(self, request): | 72 def handleFieldsRequest(self, request): |
73 """Respond to a fields request with the fields' list. | |
74 | |
75 @param request (domish.Element): the request to respond to | |
76 """ | |
75 result = domish.Element((None, 'iq')) | 77 result = domish.Element((None, 'iq')) |
76 result['type'] = 'result' | 78 result['type'] = 'result' |
77 result['id'] = request['id'] | 79 result['id'] = request['id'] |
78 result['to'] = request['from'] | 80 result['to'] = request['from'] |
79 query_elt = result.addElement('query', NS_SEARCH) | 81 query_elt = result.addElement('query', NS_SEARCH) |
81 form = data_form.Form('form', title=_('Directory search'), | 83 form = data_form.Form('form', title=_('Directory search'), |
82 instructions=FORM_INSTRUCTIONS, | 84 instructions=FORM_INSTRUCTIONS, |
83 formNamespace=NS_SEARCH) | 85 formNamespace=NS_SEARCH) |
84 form.addField(data_form.Field('fixed', label=_('Enter part of description or jid to find somebody,'))) | 86 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'))) | 87 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'))) | 88 form.addField(data_form.Field('text-single', 'jid', label=_('Jabber ID'))) |
87 form.addField(data_form.Field('text-single', 'description', label=_('Description'))) | 89 form.addField(data_form.Field('text-single', 'description', label=_('Description'))) |
88 query_elt.addChild(form.toElement()) | 90 query_elt.addChild(form.toElement()) |
89 self.xmlstream.send(result) | 91 self.xmlstream.send(result) |
90 | 92 |
91 def handleSearchRequest(self, request): | 93 def handleSearchRequest(self, request): |
92 query = ["SELECT jid, description FROM directory"] | 94 """Respond to a search request with the search results. |
95 | |
96 @param request (domish.Element): the request to respond to | |
97 """ | |
93 args = OrderedDict() | 98 args = OrderedDict() |
94 try: | 99 try: |
95 query_elt = request.elements(NS_SEARCH, 'query').next() | 100 query_elt = request.elements(NS_SEARCH, 'query').next() |
96 form_elt = query_elt.elements(data_form.NS_X_DATA, 'x').next() | 101 form_elt = query_elt.elements(data_form.NS_X_DATA, 'x').next() |
97 except StopIteration: | 102 except StopIteration: |
104 except KeyError: | 109 except KeyError: |
105 pass | 110 pass |
106 else: | 111 else: |
107 if value: | 112 if value: |
108 args[col] = value | 113 args[col] = value |
109 | 114 |
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')) | 115 result = domish.Element((None, 'iq')) |
117 result['type'] = 'result' | 116 result['type'] = 'result' |
118 result['id'] = request['id'] | 117 result['id'] = request['id'] |
119 result['to'] = request['from'] | 118 result['to'] = request['from'] |
120 query_elt = result.addElement('query', NS_SEARCH) | 119 query_elt = result.addElement('query', NS_SEARCH) |
125 jid_field_elt['label'] = 'Jabber ID' | 124 jid_field_elt['label'] = 'Jabber ID' |
126 jid_field_elt['var'] = 'jid' | 125 jid_field_elt['var'] = 'jid' |
127 description_field_elt = reported_elt.addElement('field') | 126 description_field_elt = reported_elt.addElement('field') |
128 description_field_elt['label'] = 'Description' | 127 description_field_elt['label'] = 'Description' |
129 description_field_elt['var'] = 'description' | 128 description_field_elt['var'] = 'description' |
130 for row in row_iter: | 129 |
130 for row in self._getSubscriptions(args): | |
131 for col, value in zip(('jid', 'description'), row): | 131 for col, value in zip(('jid', 'description'), row): |
132 item_elt = x_form_elt.addElement('item') | 132 item_elt = x_form_elt.addElement('item') |
133 field_elt = item_elt.addElement('field') | 133 field_elt = item_elt.addElement('field') |
134 field_elt['var'] = col | 134 field_elt['var'] = col |
135 value_elt = field_elt.addElement('value', content=value) | 135 value_elt = field_elt.addElement('value', content=value) |
136 | 136 |
137 query_elt.addChild(x_form_elt) | 137 query_elt.addChild(x_form_elt) |
138 self.xmlstream.send(result) | 138 self.xmlstream.send(result) |
139 | 139 |
140 def onSubscribe(self, request): | 140 def _getSubscriptions(self, args=None): |
141 """Retrieve the subscriptions to the directory. | |
142 | |
143 @param args (OrderedDict): arguments to filter the request | |
144 @return: list[(boolean, unicode)] | |
145 """ | |
146 query = ["SELECT jid, description FROM directory"] | |
147 | |
148 if args: | |
149 query.append("WHERE") | |
150 query.append(" AND ".join(("%s LIKE ?" % col for col in args))) | |
151 | |
152 return self.db.execute(' '.join(query), tuple(['%'+arg+'%' for arg in args.values()])) | |
153 | |
154 def _getSubscription(self, jid): | |
155 """Retrieve the subscription of the given JID. | |
156 | |
157 @param jid (jid.JID): JID to search for | |
158 @return: (boolean, unicode) | |
159 """ | |
160 return self._getSubscriptions(OrderedDict({'jid': jid.userhost()})).fetchone() | |
161 | |
162 def _setSubscription(self, from_jid, subscribed, description): | |
163 """Set the subscription of the given JID. | |
164 | |
165 @param from_jid (jid.JID): JID to subscribe (or unsubscribe) | |
166 @param subscribed (boolean): set to True to subscribe and False to unsubscribe | |
167 @param description (unicode): description | |
168 """ | |
169 if subscribed: | |
170 self.db.execute('REPLACE INTO directory(jid,description) VALUES (?,?)', (from_jid.userhost(), description)) | |
171 else: | |
172 self.db.execute('DELETE FROM directory WHERE jid=?', (from_jid.userhost(),)) | |
173 self.db.connection.commit() | |
174 | |
175 def onUpdate(self, request): | |
176 """Process the request to update a subscription. | |
177 | |
178 @param request (domish.Element): the update request | |
179 """ | |
141 result = domish.Element((None, 'iq')) | 180 result = domish.Element((None, 'iq')) |
142 result['type'] = 'result' | 181 result['type'] = 'result' |
143 result['id'] = request['id'] | 182 result['id'] = request['id'] |
144 result['to'] = request['from'] | 183 result['to'] = request['from'] |
145 from_ = jid.JID(request['from']) | 184 from_jid = jid.JID(request['from']) |
185 | |
146 request_cmd = request.elements(NS_COMMANDS, 'command').next() | 186 request_cmd = request.elements(NS_COMMANDS, 'command').next() |
147 command_elt = result.addElement('command', NS_COMMANDS) | 187 command_elt = result.addElement('command', NS_COMMANDS) |
188 command_elt['node'] = request_cmd['node'] | |
189 | |
148 try: | 190 try: |
149 session_id = request_cmd['sessionid'] | 191 session_id = request_cmd['sessionid'] |
150 except KeyError: | 192 except KeyError: |
151 session_id = None | 193 session_id = None |
152 | 194 |
153 if session_id is None: | 195 if session_id is None: |
154 # first request, we send the form | 196 # first request, we send the form |
155 command_elt['status'] = 'executing' | 197 command_elt['status'] = 'executing' |
156 session_id = str(uuid.uuid4()) | 198 session_id = str(uuid.uuid4()) |
157 actions_elt = command_elt.addElement('actions') | 199 actions_elt = command_elt.addElement("actions") |
158 actions_elt['execute'] = 'next' | 200 actions_elt['execute'] = 'complete' |
159 actions_elt.addElement('next') | 201 actions_elt.addElement('complete') |
160 form = data_form.Form('form', instructions=FORM_INSTRUCTIONS, title=_('Directory subscription')) | 202 |
161 infos = data_form.Field('fixed', value=SUBSCRIBE_TXT) | 203 subscription = self._getSubscription(from_jid) |
162 desc = data_form.Field('text-single', 'description', label=_(u"Some words about you")) | 204 subscribed, desc = ('true', subscription[1]) if subscription else ('false', '') |
163 form.addField(infos) | 205 |
164 form.addField(desc) | 206 form = data_form.Form('form', title=_('Directory subscription'), |
207 instructions=FORM_INSTRUCTIONS, | |
208 formNamespace=NS_SEARCH) | |
209 form.addField(data_form.Field('boolean', 'subscribed', label=_('Subscribed'), values=[subscribed])) | |
210 form.addField(data_form.Field('text-single', 'description', label=_(u"Some words about you"), values=[desc])) | |
165 command_elt.addChild(form.toElement()) | 211 command_elt.addChild(form.toElement()) |
166 else: | 212 else: |
167 req_forms = request_cmd.elements(data_form.NS_X_DATA, 'x') | 213 try: |
168 try: | 214 request_form = request_cmd.elements(data_form.NS_X_DATA).next() |
169 req_form = req_forms.next() | |
170 parsed_form = data_form.Form.fromElement(req_form) | |
171 description = parsed_form['description'] | |
172 except (StopIteration, KeyError): | 215 except (StopIteration, KeyError): |
173 raise ValueError # TODO: properly cancel the command | 216 raise ValueError # TODO: properly cancel the command |
174 self.db.execute('REPLACE INTO directory(jid,description) VALUES (?,?)', (from_.userhost(), description)) | 217 parsed_form = data_form.Form.fromElement(request_form) |
175 self.db.connection.commit() | 218 subscribed = parsed_form['subscribed'] in ("true", "1") |
219 description = parsed_form['description'] | |
220 self._setSubscription(from_jid, subscribed, description) | |
221 | |
176 command_elt['status'] = 'completed' | 222 command_elt['status'] = 'completed' |
177 note_elt = command_elt.addElement('note') | 223 note_elt = command_elt.addElement('note') |
178 note_elt['type'] = 'info' | 224 note_elt['type'] = 'info' |
179 note_elt.addContent(SUBSCRIBE_DONE) | 225 if subscribed: |
226 note_elt.addContent(SUBSCRIBE_DONE) | |
227 else: | |
228 note_elt.addContent(UNSUBSCRIBE_DONE) | |
229 | |
180 command_elt['sessionid'] = session_id | 230 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) | 231 self.xmlstream.send(result) |
200 | 232 |
201 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | 233 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): |
202 return [disco.DiscoFeature(NS_SEARCH), | 234 return [disco.DiscoFeature(NS_SEARCH), |
203 disco.DiscoIdentity(u"directory", u"user", u"salut"), | 235 disco.DiscoIdentity(u"directory", u"user", u"salut"), |
205 ID_CMD_LIST] | 237 ID_CMD_LIST] |
206 | 238 |
207 def getDiscoItems(self, requestor, target, nodeIdentifier=''): | 239 def getDiscoItems(self, requestor, target, nodeIdentifier=''): |
208 ret = [] | 240 ret = [] |
209 if nodeIdentifier == NS_COMMANDS: | 241 if nodeIdentifier == NS_COMMANDS: |
210 ret.append(disco.DiscoItem(target, "subscribe", "Subscribe to the directory")) | 242 ret.append(disco.DiscoItem(target, "update", "Update your subscription to the directory")) |
211 ret.append(disco.DiscoItem(target, "unsubscribe", "Unsubscribe from the directory")) | |
212 return ret | 243 return ret |