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