Mercurial > libervia-backend
comparison sat/core/patches.py @ 2691:1ecceac3df96
plugin XEP-0198: Stream Management implementation:
- hooks can now be set in stream onElement and send methods
- xmllog refactored to use new hooks
- client.isConnected now uses transport.connected method
- fixed reconnection, SàT will now try to reconnect indefinitely until it success, unresolvable failure happen (e.g. invalid certificate), or explicit disconnection is requested (or a plugin change this behaviour)
- new triggers: "stream_hooks", "disconnecting", "disconnected", and "xml_init" (replace "XML Initialized")
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 18 Nov 2018 15:49:46 +0100 |
parents | e9cd473a2f46 |
children | 00d905e1b0ef |
comparison
equal
deleted
inserted
replaced
2690:56bfe1b79204 | 2691:1ecceac3df96 |
---|---|
1 from twisted.words.protocols.jabber import xmlstream | 1 from twisted.words.protocols.jabber import xmlstream, sasl, client as tclient |
2 from twisted.internet import ssl | 2 from twisted.internet import ssl |
3 from wokkel import client | 3 from wokkel import client |
4 from sat.core.constants import Const as C | |
5 from sat.core.log import getLogger | |
4 | 6 |
5 """This module apply monkey patches to Twisted and Wokkel to handle certificate validation | 7 log = getLogger(__name__) |
6 during XMPP connection""" | |
7 | 8 |
9 """This module apply monkey patches to Twisted and Wokkel | |
10 First part handle certificate validation during XMPP connectionand are temporary | |
11 (until merged upstream). | |
12 Second part add a trigger point to send and onElement method of XmlStream | |
13 """ | |
14 | |
15 | |
16 ## certificate validation patches | |
8 | 17 |
9 class TLSInitiatingInitializer(xmlstream.TLSInitiatingInitializer): | 18 class TLSInitiatingInitializer(xmlstream.TLSInitiatingInitializer): |
10 check_certificate = True | 19 check_certificate = True |
11 | 20 |
12 def onProceed(self, obj): | 21 def onProceed(self, obj): |
19 self._deferred.callback(xmlstream.Reset) | 28 self._deferred.callback(xmlstream.Reset) |
20 | 29 |
21 | 30 |
22 class XMPPClient(client.XMPPClient): | 31 class XMPPClient(client.XMPPClient): |
23 | 32 |
24 def __init__(self, jid, password, host=None, port=5222, check_certificate=True): | 33 def __init__(self, jid, password, host=None, port=5222, |
34 check_certificate=True): | |
25 self.jid = jid | 35 self.jid = jid |
26 self.domain = jid.host.encode('idna') | 36 self.domain = jid.host.encode('idna') |
27 self.host = host | 37 self.host = host |
28 self.port = port | 38 self.port = port |
29 | 39 |
30 factory = HybridClientFactory(jid, password, check_certificate) | 40 factory = HybridClientFactory( |
41 jid, password, check_certificate=check_certificate) | |
31 | 42 |
32 client.StreamManager.__init__(self, factory) | 43 client.StreamManager.__init__(self, factory) |
33 | 44 |
34 | 45 |
35 def HybridClientFactory(jid, password, check_certificate=True): | 46 def HybridClientFactory(jid, password, check_certificate=True): |
37 | 48 |
38 return xmlstream.XmlStreamFactory(a) | 49 return xmlstream.XmlStreamFactory(a) |
39 | 50 |
40 | 51 |
41 class HybridAuthenticator(client.HybridAuthenticator): | 52 class HybridAuthenticator(client.HybridAuthenticator): |
53 res_binding = True | |
42 | 54 |
43 def __init__(self, jid, password, check_certificate): | 55 def __init__(self, jid, password, check_certificate): |
44 xmlstream.ConnectAuthenticator.__init__(self, jid.host) | 56 xmlstream.ConnectAuthenticator.__init__(self, jid.host) |
45 self.jid = jid | 57 self.jid = jid |
46 self.password = password | 58 self.password = password |
51 | 63 |
52 tlsInit = xmlstream.TLSInitiatingInitializer(xs) | 64 tlsInit = xmlstream.TLSInitiatingInitializer(xs) |
53 tlsInit.check_certificate = self.check_certificate | 65 tlsInit.check_certificate = self.check_certificate |
54 xs.initializers = [client.client.CheckVersionInitializer(xs), | 66 xs.initializers = [client.client.CheckVersionInitializer(xs), |
55 tlsInit, | 67 tlsInit, |
56 client.CheckAuthInitializer(xs)] | 68 CheckAuthInitializer(xs, self.res_binding)] |
69 | |
70 | |
71 # XmlStream triggers | |
72 | |
73 | |
74 class XmlStream(xmlstream.XmlStream): | |
75 """XmlStream which allows to add hooks""" | |
76 | |
77 def __init__(self, authenticator): | |
78 xmlstream.XmlStream.__init__(self, authenticator) | |
79 # hooks at this level should not modify content | |
80 # so it's not needed to handle priority as with triggers | |
81 self._onElementHooks = [] | |
82 self._sendHooks = [] | |
83 | |
84 def addHook(self, hook_type, callback): | |
85 """Add a send or receive hook""" | |
86 conflict_msg = (u"Hook conflict: can't add {hook_type} hook {callback}" | |
87 .format(hook_type=hook_type, callback=callback)) | |
88 if hook_type == C.STREAM_HOOK_RECEIVE: | |
89 if callback not in self._onElementHooks: | |
90 self._onElementHooks.append(callback) | |
91 else: | |
92 log.warning(conflict_msg) | |
93 elif hook_type == C.STREAM_HOOK_SEND: | |
94 if callback not in self._sendHooks: | |
95 self._sendHooks.append(callback) | |
96 else: | |
97 log.warning(conflict_msg) | |
98 else: | |
99 raise ValueError(u"Invalid hook type: {hook_type}" | |
100 .format(hook_type=hook_type)) | |
101 | |
102 def onElement(self, element): | |
103 for hook in self._onElementHooks: | |
104 hook(element) | |
105 xmlstream.XmlStream.onElement(self, element) | |
106 | |
107 def send(self, obj): | |
108 for hook in self._sendHooks: | |
109 hook(obj) | |
110 xmlstream.XmlStream.send(self, obj) | |
111 | |
112 | |
113 # Binding activation (needed for stream management, XEP-0198) | |
114 | |
115 | |
116 class CheckAuthInitializer(client.CheckAuthInitializer): | |
117 | |
118 def __init__(self, xs, res_binding): | |
119 super(CheckAuthInitializer, self).__init__(xs) | |
120 self.res_binding = res_binding | |
121 | |
122 def initialize(self): | |
123 # XXX: modification of client.CheckAuthInitializer which has optional | |
124 # resource binding, and which doesn't do deprecated | |
125 # SessionInitializer | |
126 if (sasl.NS_XMPP_SASL, 'mechanisms') in self.xmlstream.features: | |
127 inits = [(sasl.SASLInitiatingInitializer, True)] | |
128 if self.res_binding: | |
129 inits.append((tclient.BindInitializer, True)), | |
130 | |
131 for initClass, required in inits: | |
132 init = initClass(self.xmlstream) | |
133 init.required = required | |
134 self.xmlstream.initializers.append(init) | |
135 elif (tclient.NS_IQ_AUTH_FEATURE, 'auth') in self.xmlstream.features: | |
136 self.xmlstream.initializers.append( | |
137 tclient.IQAuthInitializer(self.xmlstream)) | |
138 else: | |
139 raise Exception("No available authentication method found") | |
57 | 140 |
58 | 141 |
59 def apply(): | 142 def apply(): |
143 # certificate validation | |
60 xmlstream.TLSInitiatingInitializer = TLSInitiatingInitializer | 144 xmlstream.TLSInitiatingInitializer = TLSInitiatingInitializer |
61 client.XMPPClient = XMPPClient | 145 client.XMPPClient = XMPPClient |
62 client.HybridClientFactory = HybridClientFactory | 146 # XmlStream triggers |
63 client.HybridAuthenticator = HybridAuthenticator | 147 xmlstream.XmlStreamFactory.protocol = XmlStream |