Mercurial > libervia-pubsub
annotate twisted/plugins/pubsub.py @ 414:ccb2a22ea0fc
Python 3 port:
/!\ Python 3.6+ is now needed to use SàT Pubsub
/!\ instability may occur and features may not be working anymore, this will improve with time
The same procedure as in backend has been applied (check backend commit ab2696e34d29 logs
for details).
Python minimal version has been updated in setup.py
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 16 Aug 2019 12:53:33 +0200 |
parents | a58610ab2983 |
children | 412d26a9b2c2 |
rev | line source |
---|---|
414 | 1 #!/usr/bin/env python3 |
233 | 2 #-*- coding: utf-8 -*- |
3 | |
384 | 4 # Copyright (c) 2012-2019 Jérôme Poisson |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
5 # Copyright (c) 2003-2011 Ralph Meijer |
233 | 6 |
7 | |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
8 # This program is free software: you can redistribute it and/or modify |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
9 # it under the terms of the GNU Affero General Public License as published by |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
10 # the Free Software Foundation, either version 3 of the License, or |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
11 # (at your option) any later version. |
233 | 12 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
13 # This program is distributed in the hope that it will be useful, |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
16 # GNU Affero General Public License for more details. |
233 | 17 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
18 # You should have received a copy of the GNU Affero General Public License |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
19 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
20 # -- |
233 | 21 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
22 # This program is based on Idavoll (http://idavoll.ik.nu/), |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
23 # originaly written by Ralph Meijer (http://ralphm.net/blog/) |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
24 # It is sublicensed under AGPL v3 (or any later version) as allowed by the original |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
25 # license. |
233 | 26 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
27 # -- |
233 | 28 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
29 # Here is a copy of the original license: |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
30 |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
31 # Copyright (c) 2003-2011 Ralph Meijer |
233 | 32 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
33 # Permission is hereby granted, free of charge, to any person obtaining |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
34 # a copy of this software and associated documentation files (the |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
35 # "Software"), to deal in the Software without restriction, including |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
36 # without limitation the rights to use, copy, modify, merge, publish, |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
37 # distribute, sublicense, and/or sell copies of the Software, and to |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
38 # permit persons to whom the Software is furnished to do so, subject to |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
39 # the following conditions: |
233 | 40 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
41 # The above copyright notice and this permission notice shall be |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
42 # included in all copies or substantial portions of the Software. |
233 | 43 |
312
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
44 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
45 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
46 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
47 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
48 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
49 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
5d7c3787672e
fixed copyright put in docstring instead of comments
Goffi <goffi@goffi.org>
parents:
311
diff
changeset
|
50 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
233 | 51 |
155 | 52 |
380 | 53 import csv |
369 | 54 import sat_pubsub |
380 | 55 import sys |
369 | 56 from twisted.application.service import IServiceMaker |
57 from twisted.application import service | |
380 | 58 from twisted.python import usage, log |
369 | 59 from twisted.words.protocols.jabber.jid import JID |
60 from twisted.plugin import IPlugin | |
61 | |
62 from wokkel.component import Component | |
63 from wokkel.disco import DiscoHandler | |
64 from wokkel.generic import FallbackHandler, VersionHandler | |
65 from wokkel.iwokkel import IPubSubResource | |
66 from wokkel import data_form | |
67 from wokkel import pubsub | |
68 from wokkel import rsm | |
69 from wokkel import mam | |
414 | 70 from zope.interface import implementer |
369 | 71 |
72 from sat_pubsub import const | |
73 from sat_pubsub import mam as pubsub_mam | |
382
77b52dbda89a
pubsub_admin: Pubsub Admin experimental protocol first draft:
Goffi <goffi@goffi.org>
parents:
380
diff
changeset
|
74 from sat_pubsub import pubsub_admin |
375
9a787881b824
implemented Order-By ProtoXEP (MAM + PubSub)
Goffi <goffi@goffi.org>
parents:
369
diff
changeset
|
75 from sat_pubsub.backend import BackendService, ExtraDiscoHandler |
369 | 76 from sat_pubsub.schema import SchemaHandler |
77 from sat_pubsub.privilege import PrivilegesHandler | |
78 from sat_pubsub.delegation import DelegationsHandler | |
79 from os.path import expanduser, realpath | |
414 | 80 import configparser |
369 | 81 |
82 | |
380 | 83 def coerceListType(value): |
414 | 84 return next(csv.reader( |
380 | 85 [value], delimiter=",", quotechar='"', skipinitialspace=True |
414 | 86 )) |
380 | 87 |
88 | |
89 def coerceJidListType(value): | |
90 values = [JID(v) for v in coerceListType(value)] | |
91 if any((j.resource for j in values)): | |
414 | 92 raise ValueError("you must use bare jids") |
380 | 93 return values |
94 | |
95 | |
96 | |
369 | 97 OPT_PARAMETERS_BOTH = [ |
98 ['jid', None, None, 'JID this component will be available at'], | |
99 ['xmpp_pwd', None, None, 'XMPP server component password'], | |
100 ['rhost', None, '127.0.0.1', 'XMPP server host'], | |
101 ['rport', None, '5347', 'XMPP server port'], | |
102 ['backend', None, 'pgsql', 'Choice of storage backend'], | |
103 ['db_user', None, None, 'Database user (pgsql backend)'], | |
104 ['db_name', None, 'pubsub', 'Database name (pgsql backend)'], | |
105 ['db_pass', None, None, 'Database password (pgsql backend)'], | |
106 ['db_host', None, None, 'Database host (pgsql backend)'], | |
107 ['db_port', None, None, 'Database port (pgsql backend)'], | |
108 ] | |
109 # here for future use | |
380 | 110 OPT_PARAMETERS_CFG = [ |
111 ["admins_jids_list", None, [], "List of administrators' bare jids", | |
112 coerceJidListType] | |
113 ] | |
369 | 114 |
414 | 115 CONFIG_FILENAME = 'sat' |
369 | 116 # List of the configuration filenames sorted by ascending priority |
117 CONFIG_FILES = [realpath(expanduser(path) + CONFIG_FILENAME + '.conf') for path in ( | |
118 '/etc/', '/etc/{}/'.format(CONFIG_FILENAME), | |
119 '~/', '~/.', | |
120 '.config/', '.config/.', | |
121 '', '.')] | |
122 CONFIG_SECTION = 'pubsub' | |
123 | |
124 | |
125 class Options(usage.Options): | |
126 optParameters = OPT_PARAMETERS_BOTH | |
127 | |
128 optFlags = [ | |
129 ('verbose', 'v', 'Show traffic'), | |
130 ('hide-nodes', None, 'Hide all nodes for disco') | |
131 ] | |
132 | |
133 def __init__(self): | |
134 """Read SàT Pubsub configuration file in order to overwrite the hard-coded default values. | |
135 | |
136 Priority for the usage of the values is (from lowest to highest): | |
137 - hard-coded default values | |
138 - values from SàT configuration files | |
139 - values passed on the command line | |
140 """ | |
141 # If we do it the reading later: after the command line options have been parsed, there's no good way to know | |
142 # if the options values are the hard-coded ones or if they have been passed on the command line. | |
143 | |
144 # FIXME: must be refactored + code can be factorised with backend | |
414 | 145 config_parser = configparser.ConfigParser() |
369 | 146 config_parser.read(CONFIG_FILES) |
147 for param in self.optParameters + OPT_PARAMETERS_CFG: | |
148 name = param[0] | |
149 try: | |
150 value = config_parser.get(CONFIG_SECTION, name) | |
151 try: | |
152 param[2] = param[4](value) | |
153 except IndexError: # the coerce method is optional | |
154 param[2] = value | |
380 | 155 except Exception as e: |
414 | 156 log.err('Invalid value for setting "{name}": {msg}'.format( |
380 | 157 name=name, msg=e)) |
158 sys.exit(1) | |
414 | 159 except (configparser.NoSectionError, configparser.NoOptionError): |
369 | 160 pass |
161 usage.Options.__init__(self) | |
162 for opt_data in OPT_PARAMETERS_CFG: | |
163 self[opt_data[0]] = opt_data[2] | |
155 | 164 |
369 | 165 def postOptions(self): |
166 if self['backend'] not in ['pgsql', 'memory']: | |
406 | 167 raise usage.UsageError("Unknown backend!") |
369 | 168 if self['backend'] == 'memory': |
169 raise NotImplementedError('memory backend is not available at the moment') | |
170 | |
171 self['jid'] = JID(self['jid']) if self['jid'] else None | |
172 | |
173 | |
414 | 174 @implementer(IServiceMaker, IPlugin) |
369 | 175 class SatPubsubMaker(object): |
176 tapname = "sat-pubsub" | |
414 | 177 description = "Salut à Toi Publish-Subscribe Service Component" |
369 | 178 options = Options |
179 | |
180 def makeService(self, config): | |
181 if not config['jid'] or not config['xmpp_pwd']: | |
182 raise usage.UsageError("You must specify jid and xmpp_pwd") | |
183 s = service.MultiService() | |
184 | |
185 # Create backend service with storage | |
186 | |
187 if config['backend'] == 'pgsql': | |
188 from twisted.enterprise import adbapi | |
189 from sat_pubsub.pgsql_storage import Storage | |
190 from psycopg2.extras import NamedTupleConnection | |
191 keys_map = { | |
192 'db_user': 'user', | |
193 'db_pass': 'password', | |
194 'db_name': 'database', | |
195 'db_host': 'host', | |
196 'db_port': 'port', | |
197 } | |
198 kwargs = {} | |
414 | 199 for config_k, k in keys_map.items(): |
369 | 200 v = config.get(config_k) |
201 if v is None: | |
202 continue | |
203 kwargs[k] = v | |
204 dbpool = adbapi.ConnectionPool('psycopg2', | |
205 cp_reconnect=True, | |
206 client_encoding='utf-8', | |
207 connection_factory=NamedTupleConnection, | |
208 **kwargs | |
209 ) | |
210 st = Storage(dbpool) | |
211 elif config['backend'] == 'memory': | |
406 | 212 raise NotImplementedError('memory backend is not available at the moment') |
369 | 213 |
380 | 214 bs = BackendService(st, config) |
369 | 215 bs.setName('backend') |
216 bs.setServiceParent(s) | |
217 | |
218 # Set up XMPP server-side component with publish-subscribe capabilities | |
219 | |
220 cs = Component(config["rhost"], int(config["rport"]), | |
221 config["jid"].full(), config["xmpp_pwd"]) | |
222 cs.setName('component') | |
223 cs.setServiceParent(s) | |
224 | |
225 cs.factory.maxDelay = 900 | |
226 | |
227 if config["verbose"]: | |
228 cs.logTraffic = True | |
229 | |
230 FallbackHandler().setHandlerParent(cs) | |
414 | 231 VersionHandler('SàT Pubsub', sat_pubsub.__version__).setHandlerParent(cs) |
369 | 232 DiscoHandler().setHandlerParent(cs) |
233 | |
234 ph = PrivilegesHandler(config['jid']) | |
235 ph.setHandlerParent(cs) | |
236 bs.privilege = ph | |
237 | |
238 resource = IPubSubResource(bs) | |
239 resource.hideNodes = config["hide-nodes"] | |
240 resource.serviceJID = config["jid"] | |
241 | |
242 ps = (rsm if const.FLAG_ENABLE_RSM else pubsub).PubSubService(resource) | |
243 ps.setHandlerParent(cs) | |
244 resource.pubsubService = ps | |
245 | |
246 if const.FLAG_ENABLE_MAM: | |
247 mam_resource = pubsub_mam.MAMResource(bs) | |
248 mam_s = mam.MAMService(mam_resource) | |
249 mam_s.addFilter(data_form.Field(var=const.MAM_FILTER_CATEGORY)) | |
250 mam_s.setHandlerParent(cs) | |
251 | |
382
77b52dbda89a
pubsub_admin: Pubsub Admin experimental protocol first draft:
Goffi <goffi@goffi.org>
parents:
380
diff
changeset
|
252 pa = pubsub_admin.PubsubAdminHandler(bs) |
77b52dbda89a
pubsub_admin: Pubsub Admin experimental protocol first draft:
Goffi <goffi@goffi.org>
parents:
380
diff
changeset
|
253 pa.setHandlerParent(cs) |
77b52dbda89a
pubsub_admin: Pubsub Admin experimental protocol first draft:
Goffi <goffi@goffi.org>
parents:
380
diff
changeset
|
254 |
369 | 255 sh = SchemaHandler() |
256 sh.setHandlerParent(cs) | |
257 | |
375
9a787881b824
implemented Order-By ProtoXEP (MAM + PubSub)
Goffi <goffi@goffi.org>
parents:
369
diff
changeset
|
258 # wokkel.pubsub doesn't handle non pubsub# disco |
9a787881b824
implemented Order-By ProtoXEP (MAM + PubSub)
Goffi <goffi@goffi.org>
parents:
369
diff
changeset
|
259 # and we need to announce other feature, so this is a workaround |
9a787881b824
implemented Order-By ProtoXEP (MAM + PubSub)
Goffi <goffi@goffi.org>
parents:
369
diff
changeset
|
260 # to add them |
9a787881b824
implemented Order-By ProtoXEP (MAM + PubSub)
Goffi <goffi@goffi.org>
parents:
369
diff
changeset
|
261 # FIXME: propose a patch upstream to fix this situation |
9a787881b824
implemented Order-By ProtoXEP (MAM + PubSub)
Goffi <goffi@goffi.org>
parents:
369
diff
changeset
|
262 ed = ExtraDiscoHandler() |
9a787881b824
implemented Order-By ProtoXEP (MAM + PubSub)
Goffi <goffi@goffi.org>
parents:
369
diff
changeset
|
263 ed.setHandlerParent(cs) |
9a787881b824
implemented Order-By ProtoXEP (MAM + PubSub)
Goffi <goffi@goffi.org>
parents:
369
diff
changeset
|
264 |
369 | 265 # XXX: delegation must be instancied at the end, |
266 # because it does some MonkeyPatching on handlers | |
267 dh = DelegationsHandler() | |
268 dh.setHandlerParent(cs) | |
269 bs.delegation = dh | |
270 | |
271 return s | |
272 | |
273 serviceMaker = SatPubsubMaker() |