Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 'use strict'; | |
| 6 | |
| 7 /** @suppress {duplicate} */ | |
| 8 var remoting = remoting || {}; | |
| 9 | |
| 10 /** | |
| 11 * XmppLoginHandler handles authentication handshake for XmppConnection. It | |
| 12 * receives incoming data using onDataReceived() calls |sendMessageCallback| | |
| 13 * to send outgoing messages and | |
|
Jamie
2014/08/29 02:14:09
Incomplete comment.
I think an overview of the pr
Sergey Ulanov
2014/08/29 23:40:30
Added reference to RFC and State description lists
| |
| 14 * | |
| 15 * @param {string} server Domain name of the server we are connecting to. | |
| 16 * @param {string} username Username. | |
| 17 * @param {string} authToken OAuth2 token. | |
| 18 * @param {function(string):void} sendMessageCallback Callback call to send | |
|
Jamie
2014/08/29 02:14:09
s/call/to call/
Sergey Ulanov
2014/08/29 23:40:30
Done.
| |
| 19 * a message. | |
| 20 * @param {function():void} startTlsCallback Callback to call to start TLS on | |
| 21 * the underlying socket. | |
|
Jamie
2014/08/29 02:14:09
Why does this need to be a callback? There's only
Sergey Ulanov
2014/08/29 23:40:30
This class doesn't know anything about the socket.
| |
| 22 * @param {function(string):void} onHandshakeDoneCallback Callback to call | |
| 23 * after authentication is completed successfully | |
| 24 * @param {function(Element):void} onStanzaCallback Callback to call for each | |
| 25 * incoming stanza (when authenticated). | |
| 26 * @param {function(remoting.Error, string):void} onErrorCallback Callback to | |
| 27 * call on error. Can be called at any point during lifetime of connection. | |
| 28 * @constructor | |
| 29 */ | |
| 30 remoting.XmppLoginHandler = function(server, | |
| 31 username, | |
| 32 authToken, | |
| 33 sendMessageCallback, | |
| 34 startTlsCallback, | |
| 35 onHandshakeDoneCallback, | |
| 36 onStanzaCallback, | |
| 37 onErrorCallback) { | |
| 38 this.server_ = server; | |
|
Jamie
2014/08/29 02:14:09
@private
Sergey Ulanov
2014/08/29 23:40:30
Done.
| |
| 39 this.username_ = username; | |
| 40 this.authToken_ = authToken; | |
| 41 this.sendMessageCallback_ = sendMessageCallback; | |
| 42 this.startTlsCallback_ = startTlsCallback; | |
| 43 this.onHandshakeDoneCallback_ = onHandshakeDoneCallback; | |
| 44 this.onStanzaCallback_ = onStanzaCallback; | |
| 45 this.onErrorCallback_ = onErrorCallback; | |
| 46 | |
| 47 this.state_ = remoting.XmppLoginHandler.State.INIT; | |
| 48 this.jid_ = ''; | |
| 49 | |
| 50 /** @type {remoting.XmppStreamParser} */ | |
| 51 this.streamParser_ = null; | |
| 52 } | |
| 53 | |
| 54 /** | |
| 55 * @enum {number} | |
| 56 */ | |
| 57 remoting.XmppLoginHandler.State = { | |
| 58 INIT: 0, | |
| 59 START_SENT: 1, | |
| 60 STARTTLS_SENT: 2, | |
| 61 STARTING_TLS: 3, | |
| 62 START_SENT_AFTER_TLS: 4, | |
| 63 AUTH_SENT: 5, | |
| 64 AUTH_ACCEPTED: 6, | |
| 65 BIND_SENT: 7, | |
| 66 SESSION_IQ_SENT: 8, | |
| 67 SESSION_ACTIVE: 9, | |
| 68 ERROR: 10 | |
| 69 }; | |
| 70 | |
| 71 remoting.XmppLoginHandler.prototype.start = function() { | |
| 72 this.state_ = remoting.XmppLoginHandler.State.START_SENT; | |
| 73 this.startStream_(); | |
| 74 } | |
| 75 | |
| 76 /** @param {ArrayBuffer} data */ | |
| 77 remoting.XmppLoginHandler.prototype.onDataReceived = function(data) { | |
| 78 switch (this.state_) { | |
| 79 case remoting.XmppLoginHandler.State.INIT: | |
| 80 this.onError_(remoting.Error.UNEXPECTED, | |
| 81 'Data was received before the stream was started.'); | |
| 82 break; | |
| 83 | |
| 84 case remoting.XmppLoginHandler.State.ERROR: | |
| 85 // Ignore data received in the error state. | |
| 86 break; | |
| 87 | |
| 88 default: | |
| 89 this.streamParser_.appendData(data); | |
|
kelvinp
2014/08/29 01:37:10
Have you consider moving streamParser to XMPPConne
Sergey Ulanov
2014/08/29 23:40:30
The issue here is that streamParser needs to be re
| |
| 90 break; | |
| 91 } | |
| 92 } | |
| 93 | |
| 94 /** @param {Element} stanza */ | |
| 95 remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) { | |
| 96 switch (this.state_) { | |
| 97 case remoting.XmppLoginHandler.State.START_SENT: | |
| 98 if (stanza.querySelector('features>starttls')) { | |
| 99 this.sendMessageCallback_( | |
| 100 '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>'); | |
| 101 this.state_ = remoting.XmppLoginHandler.State.STARTTLS_SENT; | |
| 102 } else { | |
| 103 this.onError_(remoting.Error.UNEXPECTED, "Server doesn't support TLS."); | |
| 104 } | |
| 105 break; | |
| 106 | |
| 107 case remoting.XmppLoginHandler.State.STARTTLS_SENT: | |
| 108 if (stanza.localName == "proceed") { | |
| 109 this.state_ = remoting.XmppLoginHandler.State.STARTING_TLS; | |
| 110 this.startTlsCallback_(); | |
| 111 } else { | |
| 112 this.onError_(remoting.Error.UNEXPECTED, | |
| 113 "Failed to start TLS: " + | |
| 114 (new XMLSerializer().serializeToString(stanza))); | |
| 115 } | |
| 116 break; | |
| 117 | |
| 118 case remoting.XmppLoginHandler.State.START_SENT_AFTER_TLS: | |
| 119 var mechanisms = Array.prototype.map.call( | |
| 120 stanza.querySelectorAll('features>mechanisms>mechanism'), | |
| 121 /** @param {Element} m */ | |
| 122 function(m) { return m.textContent; }); | |
| 123 if (mechanisms.indexOf("X-OAUTH2")) { | |
| 124 this.onError_(remoting.Error.UNEXPECTED, | |
| 125 "OAuth2 is not supported by the server."); | |
| 126 return; | |
| 127 } | |
| 128 | |
| 129 var cookie = window.btoa("\0" + this.username_ + "\0" + this.authToken_); | |
| 130 | |
| 131 this.state_ = remoting.XmppLoginHandler.State.AUTH_SENT; | |
| 132 this.sendMessageCallback_( | |
| 133 '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" ' + | |
|
Jamie
2014/08/29 02:14:09
When constructing XML hierarchies from string lite
Sergey Ulanov
2014/08/29 23:40:30
Done.
| |
| 134 'mechanism="X-OAUTH2" auth:service="oauth2" ' + | |
| 135 'auth:allow-generated-jid="true" ' + | |
| 136 'auth:client-uses-full-bind-result="true" ' + | |
| 137 'auth:allow-non-google-login="true" ' + | |
| 138 'xmlns:auth="http://www.google.com/talk/protocol/auth">' + | |
| 139 cookie + '</auth>'); | |
| 140 break; | |
| 141 | |
| 142 case remoting.XmppLoginHandler.State.AUTH_SENT: | |
| 143 if (stanza.localName == 'success') { | |
| 144 this.state_ = remoting.XmppLoginHandler.State.AUTH_ACCEPTED; | |
| 145 this.startStream_(); | |
| 146 } else { | |
| 147 this.onError_(remoting.Error.AUTHENTICATION_FAILED, | |
| 148 'Failed to authenticate: ' + | |
| 149 (new XMLSerializer().serializeToString(stanza))); | |
| 150 } | |
| 151 break; | |
| 152 | |
| 153 case remoting.XmppLoginHandler.State.AUTH_ACCEPTED: | |
| 154 if (stanza.querySelector('features>bind')) { | |
| 155 this.sendMessageCallback_( | |
| 156 '<iq type="set" id="0">' + | |
| 157 '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' + | |
| 158 '<resource>chromoting</resource></bind></iq>'); | |
| 159 this.state_ = remoting.XmppLoginHandler.State.BIND_SENT; | |
| 160 } else { | |
| 161 this.onError_(remoting.Error.UNEXPECTED, | |
| 162 'Server doesn\'t support bind after authentication.'); | |
|
Jamie
2014/08/29 02:14:09
You can use double-quotes if the string has an apo
Sergey Ulanov
2014/08/29 23:40:30
Done.
| |
| 163 } | |
| 164 break; | |
| 165 | |
| 166 case remoting.XmppLoginHandler.State.BIND_SENT: | |
| 167 var jidElement = stanza.querySelector('iq>bind>jid'); | |
| 168 if (!jidElement) | |
|
Jamie
2014/08/29 02:14:09
This is indented badly, and very confusing without
Sergey Ulanov
2014/08/29 23:40:30
Actually this |if| doesn't even need to be here.
| |
| 169 if (stanza.getAttribute('id') != '0' || | |
| 170 stanza.getAttribute('type') != 'result' || !jidElement) { | |
| 171 this.onError_(remoting.Error.UNEXPECTED, | |
| 172 'Received unexpected response to bind: ' + | |
| 173 (new XMLSerializer().serializeToString(stanza))); | |
| 174 return; | |
| 175 } | |
| 176 this.jid_ = jidElement.textContent; | |
| 177 this.sendMessageCallback_( | |
| 178 '<iq type="set" id="1">' + | |
| 179 '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>'); | |
| 180 this.state_ = remoting.XmppLoginHandler.State.SESSION_IQ_SENT; | |
| 181 break; | |
| 182 | |
| 183 case remoting.XmppLoginHandler.State.SESSION_IQ_SENT: | |
| 184 if (stanza.getAttribute('id') != '1' || | |
| 185 stanza.getAttribute('type') != 'result') { | |
| 186 this.onError_(remoting.Error.UNEXPECTED, | |
| 187 'Failed to start session: ' + | |
| 188 (new XMLSerializer().serializeToString(stanza))); | |
| 189 return; | |
| 190 } | |
| 191 this.state_ = remoting.XmppLoginHandler.State.SESSION_ACTIVE; | |
| 192 this.onHandshakeDoneCallback_(this.jid_); | |
| 193 break; | |
| 194 | |
| 195 case remoting.XmppLoginHandler.State.SESSION_ACTIVE: | |
| 196 this.onStanzaCallback_(stanza); | |
| 197 break; | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 /** @param {string} text */ | |
| 202 remoting.XmppLoginHandler.prototype.onParserError_ = function(text) { | |
| 203 this.onError_(remoting.Error.UNEXPECTED, text); | |
| 204 } | |
| 205 | |
| 206 remoting.XmppLoginHandler.prototype.onTlsStarted = function() { | |
| 207 base.debug.assert(this.state_ == | |
| 208 remoting.XmppLoginHandler.State.STARTING_TLS); | |
| 209 this.state_ = remoting.XmppLoginHandler.State.START_SENT_AFTER_TLS; | |
| 210 this.startStream_(); | |
| 211 }; | |
| 212 | |
| 213 remoting.XmppLoginHandler.prototype.startStream_ = function() { | |
| 214 this.sendMessageCallback_('<stream:stream to="' + this.server_ + | |
| 215 '" version="1.0" xmlns="jabber:client" ' + | |
| 216 'xmlns:stream="http://etherx.jabber.org/streams">'); | |
| 217 this.streamParser_ = | |
| 218 new remoting.XmppStreamParser(this.onStanza_.bind(this), | |
| 219 this.onParserError_.bind(this)); | |
| 220 } | |
| 221 | |
| 222 /** | |
| 223 * @param {remoting.Error} error | |
| 224 * @param {string} text | |
| 225 */ | |
| 226 remoting.XmppLoginHandler.prototype.onError_ = function(error, text) { | |
| 227 if (this.state_ != remoting.XmppLoginHandler.State.ERROR) { | |
| 228 this.onErrorCallback_(error, text); | |
| 229 this.state_ = remoting.XmppLoginHandler.State.ERROR; | |
| 230 } else { | |
| 231 console.error(text); | |
| 232 } | |
| 233 } | |
| OLD | NEW |