comparison sat/plugins/plugin_exp_pubsub_hook.py @ 3028:ab2696e34d29

Python 3 port: /!\ this is a huge commit /!\ starting from this commit, SàT is needs Python 3.6+ /!\ SàT maybe be instable or some feature may not work anymore, this will improve with time This patch port backend, bridge and frontends to Python 3. Roughly this has been done this way: - 2to3 tools has been applied (with python 3.7) - all references to python2 have been replaced with python3 (notably shebangs) - fixed files not handled by 2to3 (notably the shell script) - several manual fixes - fixed issues reported by Python 3 that where not handled in Python 2 - replaced "async" with "async_" when needed (it's a reserved word from Python 3.7) - replaced zope's "implements" with @implementer decorator - temporary hack to handle data pickled in database, as str or bytes may be returned, to be checked later - fixed hash comparison for password - removed some code which is not needed anymore with Python 3 - deactivated some code which needs to be checked (notably certificate validation) - tested with jp, fixed reported issues until some basic commands worked - ported Primitivus (after porting dependencies like urwid satext) - more manual fixes
author Goffi <goffi@goffi.org>
date Tue, 13 Aug 2019 19:08:41 +0200
parents 003b8b4b56a7
children 9d0df638c8b4
comparison
equal deleted inserted replaced
3027:ff5bcb12ae60 3028:ab2696e34d29
1 #!/usr/bin/env python2 1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 3
4 # SAT plugin for Pubsub Hooks 4 # SAT plugin for Pubsub Hooks
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) 5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org)
6 6
41 """Experimental plugin to launch on action on Pubsub notifications""" 41 """Experimental plugin to launch on action on Pubsub notifications"""
42 ), 42 ),
43 } 43 }
44 44
45 #  python module 45 #  python module
46 HOOK_TYPE_PYTHON = u"python" 46 HOOK_TYPE_PYTHON = "python"
47 # python file path 47 # python file path
48 HOOK_TYPE_PYTHON_FILE = u"python_file" 48 HOOK_TYPE_PYTHON_FILE = "python_file"
49 # python code directly 49 # python code directly
50 HOOK_TYPE_PYTHON_CODE = u"python_code" 50 HOOK_TYPE_PYTHON_CODE = "python_code"
51 HOOK_TYPES = (HOOK_TYPE_PYTHON, HOOK_TYPE_PYTHON_FILE, HOOK_TYPE_PYTHON_CODE) 51 HOOK_TYPES = (HOOK_TYPE_PYTHON, HOOK_TYPE_PYTHON_FILE, HOOK_TYPE_PYTHON_CODE)
52 52
53 53
54 class PubsubHook(object): 54 class PubsubHook(object):
55 def __init__(self, host): 55 def __init__(self, host):
56 log.info(_(u"PubSub Hook initialization")) 56 log.info(_("PubSub Hook initialization"))
57 self.host = host 57 self.host = host
58 self.node_hooks = {} # keep track of the number of hooks per node (for all profiles) 58 self.node_hooks = {} # keep track of the number of hooks per node (for all profiles)
59 host.bridge.addMethod( 59 host.bridge.addMethod(
60 "psHookAdd", ".plugin", in_sign="ssssbs", out_sign="", method=self._addHook 60 "psHookAdd", ".plugin", in_sign="ssssbs", out_sign="", method=self._addHook
61 ) 61 )
88 for node in client._hooks: 88 for node in client._hooks:
89 self._removeNodeManager(client, node) 89 self._removeNodeManager(client, node)
90 90
91 def _installNodeManager(self, client, node): 91 def _installNodeManager(self, client, node):
92 if node in self.node_hooks: 92 if node in self.node_hooks:
93 log.debug(_(u"node manager already set for {node}").format(node=node)) 93 log.debug(_("node manager already set for {node}").format(node=node))
94 self.node_hooks[node] += 1 94 self.node_hooks[node] += 1
95 else: 95 else:
96 # first hook on this node 96 # first hook on this node
97 self.host.plugins["XEP-0060"].addManagedNode( 97 self.host.plugins["XEP-0060"].addManagedNode(
98 node, items_cb=self._itemsReceived 98 node, items_cb=self._itemsReceived
99 ) 99 )
100 self.node_hooks[node] = 0 100 self.node_hooks[node] = 0
101 log.info(_(u"node manager installed on {node}").format(node=node)) 101 log.info(_("node manager installed on {node}").format(node=node))
102 102
103 def _removeNodeManager(self, client, node): 103 def _removeNodeManager(self, client, node):
104 try: 104 try:
105 self.node_hooks[node] -= 1 105 self.node_hooks[node] -= 1
106 except KeyError: 106 except KeyError:
107 log.error(_(u"trying to remove a {node} without hook").format(node=node)) 107 log.error(_("trying to remove a {node} without hook").format(node=node))
108 else: 108 else:
109 if self.node_hooks[node] == 0: 109 if self.node_hooks[node] == 0:
110 del self.node_hooks[node] 110 del self.node_hooks[node]
111 self.host.plugins["XEP-0060"].removeManagedNode(node, self._itemsReceived) 111 self.host.plugins["XEP-0060"].removeManagedNode(node, self._itemsReceived)
112 log.debug(_(u"hook removed")) 112 log.debug(_("hook removed"))
113 else: 113 else:
114 log.debug(_(u"node still needed for an other hook")) 114 log.debug(_("node still needed for an other hook"))
115 115
116 def installHook(self, client, service, node, hook_type, hook_arg, persistent): 116 def installHook(self, client, service, node, hook_type, hook_arg, persistent):
117 if hook_type not in HOOK_TYPES: 117 if hook_type not in HOOK_TYPES:
118 raise exceptions.DataError( 118 raise exceptions.DataError(
119 _(u"{hook_type} is not handled").format(hook_type=hook_type) 119 _("{hook_type} is not handled").format(hook_type=hook_type)
120 ) 120 )
121 if hook_type != HOOK_TYPE_PYTHON_FILE: 121 if hook_type != HOOK_TYPE_PYTHON_FILE:
122 raise NotImplementedError( 122 raise NotImplementedError(
123 _(u"{hook_type} hook type not implemented yet").format( 123 _("{hook_type} hook type not implemented yet").format(
124 hook_type=hook_type 124 hook_type=hook_type
125 ) 125 )
126 ) 126 )
127 self._installNodeManager(client, node) 127 self._installNodeManager(client, node)
128 hook_data = {"service": service, "type": hook_type, "arg": hook_arg} 128 hook_data = {"service": service, "type": hook_type, "arg": hook_arg}
134 else: 134 else:
135 hooks_list = client._hooks_temporary.setdefault(node, []) 135 hooks_list = client._hooks_temporary.setdefault(node, [])
136 hooks_list.append(hook_data) 136 hooks_list.append(hook_data)
137 137
138 log.info( 138 log.info(
139 _(u"{persistent} hook installed on {node} for {profile}").format( 139 _("{persistent} hook installed on {node} for {profile}").format(
140 persistent=_(u"persistent") if persistent else _(u"temporary"), 140 persistent=_("persistent") if persistent else _("temporary"),
141 node=node, 141 node=node,
142 profile=client.profile, 142 profile=client.profile,
143 ) 143 )
144 ) 144 )
145 145
158 # first time we get this hook, we create the callback 158 # first time we get this hook, we create the callback
159 hook_type = hook_data["type"] 159 hook_type = hook_data["type"]
160 try: 160 try:
161 if hook_type == HOOK_TYPE_PYTHON_FILE: 161 if hook_type == HOOK_TYPE_PYTHON_FILE:
162 hook_globals = {} 162 hook_globals = {}
163 execfile(hook_data["arg"], hook_globals) 163 exec(compile(open(hook_data["arg"], "rb").read(), hook_data["arg"], 'exec'), hook_globals)
164 callback = hook_globals["hook"] 164 callback = hook_globals["hook"]
165 else: 165 else:
166 raise NotImplementedError( 166 raise NotImplementedError(
167 _(u"{hook_type} hook type not implemented yet").format( 167 _("{hook_type} hook type not implemented yet").format(
168 hook_type=hook_type 168 hook_type=hook_type
169 ) 169 )
170 ) 170 )
171 except Exception as e: 171 except Exception as e:
172 log.warning( 172 log.warning(
173 _( 173 _(
174 u"Can't load Pubsub hook at node {node}, it will be removed: {reason}" 174 "Can't load Pubsub hook at node {node}, it will be removed: {reason}"
175 ).format(node=node, reason=e) 175 ).format(node=node, reason=e)
176 ) 176 )
177 hooks_list.remove(hook_data) 177 hooks_list.remove(hook_data)
178 continue 178 continue
179 179
181 try: 181 try:
182 callback(self.host, client, item) 182 callback(self.host, client, item)
183 except Exception as e: 183 except Exception as e:
184 log.warning( 184 log.warning(
185 _( 185 _(
186 u"Error while running Pubsub hook for node {node}: {msg}" 186 "Error while running Pubsub hook for node {node}: {msg}"
187 ).format(node=node, msg=e) 187 ).format(node=node, msg=e)
188 ) 188 )
189 189
190 def _addHook(self, service, node, hook_type, hook_arg, persistent, profile): 190 def _addHook(self, service, node, hook_type, hook_arg, persistent, profile):
191 client = self.host.getClient(profile) 191 client = self.host.getClient(profile)
192 service = jid.JID(service) if service else client.jid.userhostJID() 192 service = jid.JID(service) if service else client.jid.userhostJID()
193 return self.addHook( 193 return self.addHook(
194 client, 194 client,
195 service, 195 service,
196 unicode(node), 196 str(node),
197 unicode(hook_type), 197 str(hook_type),
198 unicode(hook_arg), 198 str(hook_arg),
199 persistent, 199 persistent,
200 ) 200 )
201 201
202 def addHook(self, client, service, node, hook_type, hook_arg, persistent): 202 def addHook(self, client, service, node, hook_type, hook_arg, persistent):
203 r"""Add a hook which will be triggered on a pubsub notification 203 r"""Add a hook which will be triggered on a pubsub notification
240 removed = 0 240 removed = 0
241 for hooks in (client._hooks, client._hooks_temporary): 241 for hooks in (client._hooks, client._hooks_temporary):
242 if node in hooks: 242 if node in hooks:
243 for hook_data in hooks[node]: 243 for hook_data in hooks[node]:
244 if ( 244 if (
245 service != hook_data[u"service"] 245 service != hook_data["service"]
246 or hook_type is not None 246 or hook_type is not None
247 and hook_type != hook_data[u"type"] 247 and hook_type != hook_data["type"]
248 or hook_arg is not None 248 or hook_arg is not None
249 and hook_arg != hook_data[u"arg"] 249 and hook_arg != hook_data["arg"]
250 ): 250 ):
251 continue 251 continue
252 hooks[node].remove(hook_data) 252 hooks[node].remove(hook_data)
253 removed += 1 253 removed += 1
254 if not hooks[node]: 254 if not hooks[node]:
261 return removed 261 return removed
262 262
263 def _listHooks(self, profile): 263 def _listHooks(self, profile):
264 hooks_list = self.listHooks(self.host.getClient(profile)) 264 hooks_list = self.listHooks(self.host.getClient(profile))
265 for hook in hooks_list: 265 for hook in hooks_list:
266 hook[u"service"] = hook[u"service"].full() 266 hook["service"] = hook["service"].full()
267 hook[u"persistent"] = C.boolConst(hook[u"persistent"]) 267 hook["persistent"] = C.boolConst(hook["persistent"])
268 return hooks_list 268 return hooks_list
269 269
270 def listHooks(self, client): 270 def listHooks(self, client):
271 """return list of registered hooks""" 271 """return list of registered hooks"""
272 hooks_list = [] 272 hooks_list = []
273 for hooks in (client._hooks, client._hooks_temporary): 273 for hooks in (client._hooks, client._hooks_temporary):
274 persistent = hooks is client._hooks 274 persistent = hooks is client._hooks
275 for node, hooks_data in hooks.iteritems(): 275 for node, hooks_data in hooks.items():
276 for hook_data in hooks_data: 276 for hook_data in hooks_data:
277 hooks_list.append( 277 hooks_list.append(
278 { 278 {
279 u"service": hook_data[u"service"], 279 "service": hook_data["service"],
280 u"node": node, 280 "node": node,
281 u"type": hook_data[u"type"], 281 "type": hook_data["type"],
282 u"arg": hook_data[u"arg"], 282 "arg": hook_data["arg"],
283 u"persistent": persistent, 283 "persistent": persistent,
284 } 284 }
285 ) 285 )
286 return hooks_list 286 return hooks_list